diff --git a/configure.ac b/configure.ac
index a972c327e..77671e2c5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3,12 +3,12 @@ AC_PREREQ([2.60])
define(_CLIENT_VERSION_MAJOR, 3)
dnl Must be kept in sync with src/clientversion.h , ugh!
define(_CLIENT_VERSION_MINOR, 10)
-define(_CLIENT_VERSION_REVISION, 0)
+define(_CLIENT_VERSION_REVISION, 1)
define(_CLIENT_VERSION_BUILD, 50)
define(_ZC_BUILD_VAL, m4_if(m4_eval(_CLIENT_VERSION_BUILD < 25), 1, m4_incr(_CLIENT_VERSION_BUILD), m4_eval(_CLIENT_VERSION_BUILD < 50), 1, m4_eval(_CLIENT_VERSION_BUILD - 24), m4_eval(_CLIENT_VERSION_BUILD == 50), 1, , m4_eval(_CLIENT_VERSION_BUILD - 50)))
define(_CLIENT_VERSION_SUFFIX, m4_if(m4_eval(_CLIENT_VERSION_BUILD < 25), 1, _CLIENT_VERSION_REVISION-beta$1, m4_eval(_CLIENT_VERSION_BUILD < 50), 1, _CLIENT_VERSION_REVISION-rc$1, m4_eval(_CLIENT_VERSION_BUILD == 50), 1, _CLIENT_VERSION_REVISION, _CLIENT_VERSION_REVISION-$1)))
define(_CLIENT_VERSION_IS_RELEASE, true)
-define(_COPYRIGHT_YEAR, 2023)
+define(_COPYRIGHT_YEAR, 2024)
AC_INIT([Hush],[_CLIENT_VERSION_MAJOR._CLIENT_VERSION_MINOR._CLIENT_VERSION_SUFFIX(_ZC_BUILD_VAL)],[https://git.hush.is/hush/hush3],[hush])
AC_CONFIG_SRCDIR([src/main.cpp])
AC_CONFIG_HEADERS([src/config/bitcoin-config.h])
diff --git a/contrib/block_time.pl b/contrib/block_time.pl
index 1b7c6a914..0a9bf6e53 100755
--- a/contrib/block_time.pl
+++ b/contrib/block_time.pl
@@ -7,14 +7,18 @@ use strict;
# Given a block time, estimate when it will happen
my $block = shift || die "Usage: $0 123";
+my $coin = shift || '';
my $hush = "./src/hush-cli";
unless (-e $hush) {
die "$hush does not exist, aborting";
}
+if ($coin) {
+ $hush .= " -ac_name=$coin";
+}
my $blockcount = qx{$hush getblockcount};
unless ($blockcount = int($blockcount)) {
- print "Invalid response from hush-cli\n";
+ print "Invalid response from $hush\n";
exit 1;
}
@@ -22,13 +26,24 @@ if ($block <= $blockcount) {
die "That block has already happened!";
} else {
my $diff = $block - $blockcount;
- my $minutes = $diff*1.25; # 75s in minutes
+ my $minpb = 1.25; # 75s in minutes for HUSH3
+ if ($coin eq 'DRAGONX') {
+ $minpb = 0.6; # minutes per block
+ } elsif ($coin) {
+ # TODO: support custom bloctimes
+ $minpb = 1; # assumes default blocktime of 60s
+ }
+ my $minutes = $diff*$minpb;
my $seconds = $minutes*60;
my $now = time;
my $then = $now + $seconds;
my $ldate = localtime($then);
my $gmdate = gmtime($then);
- print "Hush Block $block will happen at roughly:\n";
+ if ($coin) {
+ print "$coin Block $block will happen at roughly:\n";
+ } else {
+ print "Hush Block $block will happen at roughly:\n";
+ }
print "$ldate Eastern # $then\n";
print "$gmdate GMT # $then\n";
}
diff --git a/depends/packages/boost.mk b/depends/packages/boost.mk
index 0062c0bdb..f7d34c253 100644
--- a/depends/packages/boost.mk
+++ b/depends/packages/boost.mk
@@ -1,9 +1,12 @@
package=boost
$(package)_version=1_72_0
-$(package)_download_path=https://boostorg.jfrog.io/artifactory/main/release/$(subst _,.,$($(package)_version))/source/
+#$(package)_download_path=https://boostorg.jfrog.io/artifactory/main/release/$(subst _,.,$($(package)_version))/source/
+#$(package)_file_name=$(package)_$($(package)_version).tar.bz2
$(package)_sha256_hash=59c9b274bc451cf91a9ba1dd2c7fdcaf5d60b1b3aa83f2c9fa143417cc660722
-$(package)_file_name=$(package)_$($(package)_version).tar.bz2
+$(package)_download_path=https://git.hush.is/attachments
+$(package)_file_name=7b13759e-8623-4e48-ae08-f78502f4b6a5
+$(package)_download_file=7b13759e-8623-4e48-ae08-f78502f4b6a5
$(package)_patches=fix-Solaris.patch
define $(package)_set_vars
diff --git a/depends/packages/libcurl.mk b/depends/packages/libcurl.mk
index 92c319a70..96c41e0b5 100644
--- a/depends/packages/libcurl.mk
+++ b/depends/packages/libcurl.mk
@@ -14,7 +14,7 @@ $(package)_dependencies=wolfssl
$(package)_download_path=https://curl.haxx.se/download
$(package)_config_opts_linux=--disable-shared --enable-static --without-ssl --prefix=$(host_prefix) --host=$(host)
$(package)_config_opts_mingw32=--enable-mingw --disable-shared --enable-static --with-wolfssl --without-ssl --prefix=$(host_prefix) --host=x86_64-w64-mingw32
-$(package)_config_opts_darwin=--disable-shared --enable-static --with-wolfssl --without-ssl --prefix=$(host_prefix)
+$(package)_config_opts_darwin=--disable-shared --enable-static --without-ssl --prefix=$(host_prefix)
$(package)_cflags_darwin=-mmacosx-version-min=10.9
$(package)_conf_tool=./configure
diff --git a/doc/cjdns.md b/doc/cjdns.md
new file mode 100644
index 000000000..4b47bb8ac
--- /dev/null
+++ b/doc/cjdns.md
@@ -0,0 +1,115 @@
+# CJDNS support in Hush
+
+It is possible to run Hush over CJDNS, an encrypted IPv6 network that
+uses public-key cryptography for address allocation and a distributed hash table
+for routing.
+
+## What is CJDNS?
+
+CJDNS is like a distributed, shared VPN with multiple entry points where every
+participant can reach any other participant. All participants use addresses from
+the `fc00::/8` network (reserved IPv6 range). Installation and configuration is
+done outside of Hush, similarly to a VPN (either in the host/OS or on
+the network router). See https://github.com/cjdelisle/cjdns#readme and
+https://github.com/hyperboria/docs#hyperboriadocs for more information.
+
+Compared to IPv4/IPv6, CJDNS provides end-to-end encryption and protects nodes
+from traffic analysis and filtering.
+
+Used with Tor and I2P, CJDNS is a complementary option that can enhance network
+redundancy and robustness for both the Hush network and individual nodes.
+
+Each network has different characteristics. For instance, Tor is widely used but
+somewhat centralized. I2P connections have a source address and I2P is slow.
+CJDNS is fast but does not hide the sender and the recipient from intermediate
+routers.
+
+## Installing CJDNS and finding a peer to connect to the network
+
+To install and set up CJDNS, follow the instructions at
+https://github.com/cjdelisle/cjdns#how-to-install-cjdns.
+
+You need to initiate an outbound connection to a peer on the CJDNS network
+before it will work with your Hush node. This is described in steps
+["2. Find a friend"](https://github.com/cjdelisle/cjdns#2-find-a-friend) and
+["3. Connect your node to your friend's
+node"](https://github.com/cjdelisle/cjdns#3-connect-your-node-to-your-friends-node)
+in the CJDNS documentation.
+
+One quick way to accomplish these two steps is to query for available public
+peers on [Hyperboria](https://github.com/hyperboria) by running the following:
+
+```
+git clone https://github.com/hyperboria/peers hyperboria-peers
+cd hyperboria-peers
+./testAvailable.py
+```
+
+For each peer, the `./testAvailable.py` script prints the filename of the peer's
+credentials followed by the ping result.
+
+Choose one or several peers, copy their credentials from their respective files,
+paste them into the relevant IPv4 or IPv6 "connectTo" JSON object in the
+`cjdroute.conf` file you created in step ["1. Generate a new configuration
+file"](https://github.com/cjdelisle/cjdns#1-generate-a-new-configuration-file),
+and save the file.
+
+## Launching CJDNS
+
+Typically, CJDNS might be launched from its directory with
+`sudo ./cjdroute < cjdroute.conf` and it sheds permissions after setting up the
+[TUN](https://en.wikipedia.org/wiki/TUN/TAP) interface. You may also [launch it as an
+unprivileged user](https://github.com/cjdelisle/cjdns/blob/master/doc/non-root-user.md)
+with some additional setup.
+
+The network connection can be checked by running `./tools/peerStats` from the
+CJDNS directory.
+
+## Run Hush with CJDNS
+
+Once you are connected to the CJDNS network, the following Hush
+configuration option makes CJDNS peers automatically reachable:
+
+```
+-cjdnsreachable
+```
+
+When enabled, this option tells Hush that it is running in an
+environment where a connection to an `fc00::/8` address will be to the CJDNS
+network instead of to an [RFC4193](https://datatracker.ietf.org/doc/html/rfc4193)
+IPv6 local network. This helps Hush perform better address management:
+ - Your node can consider incoming `fc00::/8` connections to be from the CJDNS
+ network rather than from an IPv6 private one.
+ - If one of your node's local addresses is `fc00::/8`, then it can choose to
+ gossip that address to peers.
+
+## Additional configuration options related to CJDNS
+
+```
+-onlynet=cjdns
+```
+
+Make automatic outbound connections only to CJDNS addresses. Inbound and manual
+connections are not affected by this option. It can be specified multiple times
+to allow multiple networks, e.g. onlynet=cjdns, onlynet=i2p, onlynet=onion.
+
+CJDNS support was added to Hush in version 3.9.3 and there may be fewer
+CJDNS peers than Tor or IP ones. You can use `hush-cli -addrinfo` to see the
+number of CJDNS addresses known to your node.
+
+In general, a node can be run with both an onion service and CJDNS (or any/all
+of IPv4/IPv6/onion/I2P/CJDNS), which can provide a potential fallback if one of
+the networks has issues. There are a number of ways to configure this; see
+[doc/tor.md](https://git.hush.is/hush/hush3/src/branch/master/doc/tor.md) for
+details.
+
+## CJDNS-related information in Hush
+
+There are several ways to see your CJDNS address in Hush:
+- in the "Local addresses" output of CLI `-netinfo`
+- in the "localaddresses" output of RPC `getnetworkinfo`
+
+To see which CJDNS peers your node is connected to, use `hush-cli -netinfo 4`
+or the `getpeerinfo` RPC (i.e. `hush-cli getpeerinfo`).
+
+You can use the `getnodeaddresses` RPC to fetch a number of CJDNS peers known to your node; run `hush-cli help getnodeaddresses` for details.
diff --git a/doc/config.md b/doc/config.md
index 44df5ece3..e91b2f48d 100644
--- a/doc/config.md
+++ b/doc/config.md
@@ -38,10 +38,30 @@ Defaults to 1. This is a default option that should not be changed or things wil
Defaults to 0. This option enables the "shielded index" which also calculates the "anonset" (anonymity set) also known as the "shielded pool". This data is avaailable in the getchaintxstats RPC, if zindex is enabled. Enabling this feature requires a full rescan or full sync from scratch, which is not done by default. If you don't do one of those things, your zindex stats will be incorrect.
-# Mining options
+# Mining and Stratum server options
These options are only of interest to solo miners and mining pool operators....
+## stratum
+
+Defaults to off. This option enables a Stratum server.
+
+## stratumaddress=
+
+Defaults to none. This option sets a Stratum Mining address to use when special address of 'x' is sent by miner.
+
+## stratumbind=
+
+Defaults to: bind to all interfaces. This option Binds to given address to listen for Stratum work requests. Use [host]:port notation for IPv6. This option can be specified multiple times.
+
+## stratumport=
+
+Defaults to 19031 or 19031 for testnet. This option sets the to listen for Stratum work requests on.
+
+## stratumallowip=
+
+No default. This option allows Stratum work requests from specified source. Valid for are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times.
+
# Other options
These options are not commonly used and likely on for advanced users and/or developers...
@@ -56,4 +76,4 @@ Defaults to 0 in hushd, defaults to 1 in some GUI wallets. Maintain a timestamp
## spentindex=1
-Defaults to 0 in hushd, defaults to 1 in some GUI wallets. Maintain a full spent index, used to query the spending txid and input index for an outpoint
\ No newline at end of file
+Defaults to 0 in hushd, defaults to 1 in some GUI wallets. Maintain a full spent index, used to query the spending txid and input index for an outpoint
diff --git a/doc/i2p.md b/doc/i2p.md
index e01573f9d..583f9ddb2 100644
--- a/doc/i2p.md
+++ b/doc/i2p.md
@@ -33,12 +33,10 @@ configuration options:
none)
-i2pacceptincoming
- If set and -i2psam is also set then incoming I2P connections are
- accepted via the SAM proxy. If this is not set but -i2psam is set
- then only outgoing connections will be made to the I2P network.
- Ignored if -i2psam is not set. Listening for incoming I2P
- connections is done through the SAM proxy, not by binding to a
- local address and port (default: 1)
+ Whether to accept inbound I2P connections (default: 1). Ignored if
+ -i2psam is not set. Listening for inbound I2P connections is
+ done through the SAM proxy, not by binding to a local address and
+ port.
```
In a typical situation, this suffices:
@@ -56,6 +54,36 @@ connections if `-i2pacceptincoming=1`. If `-i2pacceptincoming=0` then only
outbound I2P connections are made and a different transient I2P address is used
for each connection to improve privacy.
+## Additional configuration options related to I2P
+
+```
+-debug=i2p
+```
+
+Set the `debug=i2p` config logging option to see additional information in the
+debug log about your I2P configuration and connections.
+
+```
+-onlynet=i2p
+```
+
+Make automatic outbound connections only to I2P addresses. Inbound and manual
+connections are not affected by this option. It can be specified multiple times
+to allow multiple networks, e.g. onlynet=onion, onlynet=i2p.
+
+I2P support was added to Hush in version 3.9.3 and there may be fewer I2P
+peers than Tor or IP ones. Therefore, using I2P alone without other networks may
+make a node more susceptible to [Sybil
+attacks](https://en.bitcoin.it/wiki/Weaknesses#Sybil_attack).
+
+Another consideration with `onlynet=i2p` is that the initial blocks download
+phase when syncing up a new node can be very slow. This phase can be sped up by
+using other networks, for instance `onlynet=onion`, at the same time.
+
+In general, a node can be run with both onion and I2P hidden services (or
+any/all of IPv4/IPv6/onion/I2P/CJDNS), which can provide a potential fallback if
+one of the networks has issues.
+
## Persistent vs transient I2P addresses
In I2P connections, the connection receiver sees the I2P address of the
@@ -136,14 +164,19 @@ port (`TO_PORT`) is always set to 0 and is not in the control of Hush.
## Bandwidth
-I2P routers may route a large amount of general network traffic with their
-default settings. Check your router's configuration to limit the amount of this
-traffic relayed, if desired.
+By default, your node shares bandwidth and transit tunnels with the I2P network
+in order to increase your anonymity with cover traffic, help the I2P router used
+by your node integrate optimally with the network, and give back to the network.
+It's important that the nodes of a popular application like Hush contribute
+as much to the I2P network as they consume.
-With `i2pd`, the amount of bandwidth being shared with the wider network can be
-adjusted with the `bandwidth`, `share` and `transittunnels` options in your
-`i2pd.conf` file. For example, to limit total I2P traffic to 256KB/s and share
-50% of this limit for a maximum of 20 transit tunnels:
+It is possible, though strongly discouraged, to change your I2P router
+configuration to limit the amount of I2P traffic relayed by your node.
+
+With `i2pd`, this can be done by adjusting the `bandwidth`, `share` and
+`transittunnels` options in your `i2pd.conf` file. For example, to limit total
+I2P traffic to 256KB/s and share 50% of this limit for a maximum of 20 transit
+tunnels:
```
bandwidth = 256
@@ -153,9 +186,15 @@ share = 50
transittunnels = 20
```
-If you prefer not to relay any public I2P traffic and only permit I2P traffic
-from programs which are connecting via the SAM proxy, e.g. Hush, you
-can set the `notransit` option to `true`.
-
Similar bandwidth configuration options for the Java I2P router can be found in
`http://127.0.0.1:7657/config` under the "Bandwidth" tab.
+
+Before doing this, please see the "Participating Traffic Considerations" section
+in [Embedding I2P in your Application](https://geti2p.net/en/docs/applications/embedding).
+
+In most cases, the default router settings should work fine.
+
+## Bundling I2P in a Hush application
+
+Please see the "General Guidance for Developers" section in https://geti2p.net/en/docs/api/samv3
+if you are developing a downstream application that may be bundling I2P with Hush.
diff --git a/doc/man/hush-cli.1 b/doc/man/hush-cli.1
index a82565e74..c2e81580b 100644
--- a/doc/man/hush-cli.1
+++ b/doc/man/hush-cli.1
@@ -1,9 +1,9 @@
-.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.6.
-.TH HUSH-CLI "1" "June 2023" "hush-cli v3.9.4" "User Commands"
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13.
+.TH HUSH-CLI "1" "January 2024" "hush-cli v3.10.1" "User Commands"
.SH NAME
-hush-cli \- manual page for hush-cli v3.9.4
+hush-cli \- manual page for hush-cli v3.10.1
.SH DESCRIPTION
-Hush RPC client version v3.9.4\-44595d5ab
+Hush RPC client version v3.10.1\-05ee31891\-dirty
.PP
In order to ensure you are adequately protecting your privacy when using Hush,
please see .
@@ -75,7 +75,7 @@ Read extra arguments from standard input, one per line until EOF/Ctrl\-D
In order to ensure you are adequately protecting your privacy when using Hush,
please see .
-Copyright (C) 2016-2023 Duke Leto and The Hush Developers
+Copyright (C) 2016-2024 Duke Leto and The Hush Developers
Copyright (C) 2016-2020 jl777 and SuperNET developers
diff --git a/doc/man/hush-tx.1 b/doc/man/hush-tx.1
index a4bc4033b..5c1867b46 100644
--- a/doc/man/hush-tx.1
+++ b/doc/man/hush-tx.1
@@ -1,9 +1,9 @@
-.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.6.
-.TH HUSH-TX "1" "June 2023" "hush-tx v3.9.4" "User Commands"
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13.
+.TH HUSH-TX "1" "January 2024" "hush-tx v3.10.1" "User Commands"
.SH NAME
-hush-tx \- manual page for hush-tx v3.9.4
+hush-tx \- manual page for hush-tx v3.10.1
.SH DESCRIPTION
-hush\-tx utility version v3.9.4\-44595d5ab
+hush\-tx utility version v3.10.1\-05ee31891\-dirty
.SS "Usage:"
.TP
hush\-tx [options] [commands]
@@ -89,7 +89,7 @@ Set register NAME to given JSON\-STRING
In order to ensure you are adequately protecting your privacy when using Hush,
please see .
-Copyright (C) 2016-2023 Duke Leto and The Hush Developers
+Copyright (C) 2016-2024 Duke Leto and The Hush Developers
Copyright (C) 2016-2020 jl777 and SuperNET developers
diff --git a/doc/man/hushd.1 b/doc/man/hushd.1
index 627c9c651..2f6fe2d2a 100644
--- a/doc/man/hushd.1
+++ b/doc/man/hushd.1
@@ -1,9 +1,9 @@
-.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.6.
-.TH HUSHD "1" "June 2023" "hushd v3.9.4" "User Commands"
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13.
+.TH HUSHD "1" "January 2024" "hushd v3.10.1" "User Commands"
.SH NAME
-hushd \- manual page for hushd v3.9.4
+hushd \- manual page for hushd v3.10.1
.SH DESCRIPTION
-Hush Daemon version v3.9.4\-44595d5ab
+Hush Daemon version v3.10.1\-05ee31891\-dirty
.PP
In order to ensure you are adequately protecting your privacy when using Hush,
please see .
@@ -78,7 +78,7 @@ applied)
.HP
\fB\-par=\fR
.IP
-Set the number of script verification threads (\fB\-6\fR to 16, 0 = auto, <0 =
+Set the number of script verification threads (\fB\-8\fR to 16, 0 = auto, <0 =
leave that many cores free, default: 0)
.HP
\fB\-pid=\fR
@@ -336,6 +336,10 @@ Set key pool size to (default: 100)
.IP
Enable auto Sapling note consolidation (default: false)
.HP
+\fB\-consolidationinterval\fR
+.IP
+Block interval between consolidations (default: 25)
+.HP
\fB\-consolidatesaplingaddress=\fR
.IP
Specify Sapling Address to Consolidate. (default: all)
@@ -767,7 +771,7 @@ Enforce transaction\-rate limit, default 0
In order to ensure you are adequately protecting your privacy when using Hush,
please see .
-Copyright (C) 2016-2023 Duke Leto and The Hush Developers
+Copyright (C) 2016-2024 Duke Leto and The Hush Developers
Copyright (C) 2016-2020 jl777 and SuperNET developers
diff --git a/doc/overview.md b/doc/overview.md
index 334cfe84b..27d991754 100644
--- a/doc/overview.md
+++ b/doc/overview.md
@@ -14,7 +14,24 @@ Equihash (200,9) (ASIC)
## P2P
-TLS1.3 via WolfSSL is enforced for all network connections as of v3.6.1
+TLS1.3 via WolfSSL is enforced for all network connections as of v3.6.1 .
+Many ciphersuites are technically supported by TLS1.3 but many of them
+are ancient, proved to be less secure than intended or likely backdoored.
+Hush only uses what are widely considered to be the most secure and [best ciphersuites](https://ciphersuite.info/cs/).
+
+New Hush P2P connections randomly choose between these two ciphersuites each
+time a new connection to a peer is created:
+
+ * `TLS_AES_256_GCM_SHA384`
+ * `TLS_CHACHA20_POLY1305_SHA256`
+
+Encrypted P2P connections are important because it means passive network spies,
+such as ISPs, cannot tell what nodes are communicating to each other and also
+prevents certain attacks against privacy at the network level, such as looking
+for which node was the first to relay a transaction. Bitcoin has no protection
+against this which is why it's trivial for network spies to tell which node
+(and hence which IP address) created a certain transaction and hence which
+IP address owns which addresses.
## RPC
diff --git a/doc/release-process.md b/doc/release-process.md
index c25792fbf..566de1b1f 100644
--- a/doc/release-process.md
+++ b/doc/release-process.md
@@ -26,7 +26,8 @@ If the last command has no output, congrats, there is nothing to do. If the last
```
git checkout master
git merge --no-ff dev # using the default commit message is fine
-git push origin master
+git tag vX.Y.Z # this creates a tag vX.Y.Z on current master, or you can let gitea do it later
+git push --tags origin master
git checkout dev
git merge master
git push origin dev
@@ -129,4 +130,4 @@ Use `./util/build-debian-package.sh aarch64` to build a Debian package for aarch
### Updating RandomX
-If you need to update the source code of our in tree copy of RandomX, see issue https://git.hush.is/hush/hush3/issues/337#issuecomment-5114 for details. Currently we use RandomX v1.2.1 from the official repo at https://github.com/tevador/RandomX/releases/tag/v1.2.1
\ No newline at end of file
+If you need to update the source code of our in tree copy of RandomX, see issue https://git.hush.is/hush/hush3/issues/337#issuecomment-5114 for details. Currently we use RandomX v1.2.1 from the official repo at https://github.com/tevador/RandomX/releases/tag/v1.2.1
diff --git a/doc/relnotes/README.md b/doc/relnotes/README.md
index 8c5f45b7d..ff09ce3a1 100644
--- a/doc/relnotes/README.md
+++ b/doc/relnotes/README.md
@@ -10,12 +10,35 @@ and no longer on Github, since they banned Duke Leto and
also because they censor many people around the world and work with
evil organizations.
-# Hush 3.10.0 ""
+# Hush 3.10.1 "Oneiric Octopus"
-This is a MANDATORY release for Hush and nodes must upgrade by block height X, which will happen
-on approximately Y. This is an OPTIONAL release for DragonX but it is highly recommended for
-exchanges, solo miners and mining pools to update to this release.
+This is an OPTIONAL but RECOMMENDED release for Hush full nodes. It fixes an important bug
+that affects new Hush nodes. It also makes syncing faster for both Hushd and DragonX nodes.
+```
+ 26 files changed, 257 insertions(+), 429 deletions(-)
+```
+
+ * Fix the bug which causes "payment to wrong pubkey" error when syncing a new node
+ * Faster syncing of Hush and DragonX full nodes
+ * Slightly less memory usage in each Equihash/RandomX mining thread
+ * Fixed compiling issues related to RandomX v1.2.1
+ * Improved RPC docs for `getblocktemplate`
+ * Removed the `getdeprecationinfo` RPC
+
+
+# Hush 3.10.0 "Sassy Siphonophore"
+
+```
+132 files changed, 6387 insertions(+), 2084 deletions(-)
+```
+
+This is a MANDATORY release for Hush and *ALL* nodes must upgrade by block height 1605555, which will happen
+on approximately Dec 16th 2023. YOU MUST UPGRADE YOUR HUSH FULL NODE TO THIS RELEASE BY DEC 15th 2023.
+If you do not, your node will not work correctly and will require a fresh sync to fix.
+
+This is an OPTIONAL release for DragonX but it is highly recommended for miners and exchanges
+to update to this release.
* Hush and all Hush Smart Chains now use less RAM https://git.hush.is/hush/hush3/issues/283
* Hush full nodes will use ~2GB less RAM
@@ -26,8 +49,11 @@ exchanges, solo miners and mining pools to update to this release.
* Fix a bug where `hush-cli stop` would not stop the node during the "Building Witnesses" rescan phase https://git.hush.is/hush/hush3/issues/330
* Fix bugs where `abortrescan` couldn't be used when node is start up (RPC warmup) and where it could not abort the rescan if it was in the "Building Witnesses" phase https://git.hush.is/hush/hush3/issues/331
* Fix bug in `z_mergetoaddress` where docs said you could use `ANY_ZADDR` but you couldn't https://git.hush.is/hush/hush3/commit/7eb9d75b94469c3fc8c028f29b35be9ac764a10c
+ * RPC `z_listunspent` now returns the text representation of a memo in `memoStr` key
* Upgraded curl to 8.4.0 https://git.hush.is/hush/hush3/issues/325
* This fixes CVE-2023-38545 which affects very few or potentially no Hush/DragonX users. It could only affect people who compile Hush full node software (not those who use binaries or packages) and who use a malicious SOCKS5 proxy for all network traffic via the operating system.
+ * New documentation about using CJDNS with Hush: https://git.hush.is/hush/hush3/src/branch/dev/doc/cjdns.md
+ * Decentralized Devtax for improved scalability and operational security. This is a consensus change.
* DragonX specific changes:
* Updated to latest RandomX v1.2.1 which includes mining optimizations https://git.hush.is/hush/hush3/commit/6029b3d571009991ae9c4aea0397f4d00be6a817 https://git.hush.is/hush/hush3/issues/337
* Fix RandomX mining memory leak and crash https://git.hush.is/hush/hush3/issues/324
@@ -38,7 +64,6 @@ exchanges, solo miners and mining pools to update to this release.
* For instance, many RPCs such as `dragonx z_sendmany ...` would not previously work because the arguments to the RPC were not quoted correctly.
-
# Hush 3.9.4 "Maniacal Manticore"
```
diff --git a/qa/rpc-tests/wallet.py b/qa/rpc-tests/wallet.py
index c50c028a3..2047f9e0b 100755
--- a/qa/rpc-tests/wallet.py
+++ b/qa/rpc-tests/wallet.py
@@ -221,7 +221,7 @@ class WalletTest (BitcoinTestFramework):
for uTx in unspentTxs:
if uTx['txid'] == zeroValueTxid:
found = True
- assert_equal(uTx['amount'], Decimal('0.00000000'))
+ assert_equal(uTx['amount'], Decimal('0'))
assert(found)
#do some -walletbroadcast tests
diff --git a/src/Makefile.am b/src/Makefile.am
index 6178fa19e..2e890ebd7 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -165,7 +165,6 @@ BITCOIN_CORE_H = \
consensus/validation.h \
core_io.h \
core_memusage.h \
- deprecation.h \
fs.h \
hash.h \
httprpc.h \
@@ -273,26 +272,15 @@ libbitcoin_server_a_SOURCES = \
asyncrpcqueue.cpp \
bloom.cpp \
cc/eval.cpp \
- cc/import.cpp \
- cc/importgateway.cpp \
cc/CCassetsCore.cpp \
cc/CCcustom.cpp \
cc/CCtx.cpp \
cc/CCutils.cpp \
- cc/CCtokens.cpp \
cc/assets.cpp \
cc/faucet.cpp \
- cc/rewards.cpp \
- cc/dice.cpp \
- cc/lotto.cpp \
cc/fsm.cpp \
cc/heir.cpp \
cc/oracles.cpp \
- cc/prices.cpp \
- cc/pegs.cpp \
- cc/payments.cpp \
- cc/gateways.cpp \
- cc/channels.cpp \
cc/auction.cpp \
cc/betprotocol.cpp \
chain.cpp \
@@ -300,7 +288,6 @@ libbitcoin_server_a_SOURCES = \
fs.cpp \
crosschain.cpp \
crosschain_authority.cpp \
- deprecation.cpp \
httprpc.cpp \
httpserver.cpp \
i2p.cpp \
@@ -349,7 +336,6 @@ libbitcoin_wallet_a_SOURCES = \
zcash/Note.cpp \
transaction_builder.cpp \
wallet/rpcdump.cpp \
- cc/CCtokens.cpp \
cc/CCassetsCore.cpp \
cc/CCassetstx.cpp \
cc/CCtx.cpp \
@@ -416,7 +402,6 @@ libbitcoin_common_a_SOURCES = \
core_read.cpp \
core_write.cpp \
hash.cpp \
- importcoin.cpp \
key.cpp \
key_io.cpp \
keystore.cpp \
@@ -473,10 +458,6 @@ if GLIBC_BACK_COMPAT
libbitcoin_util_a_SOURCES += compat/glibc_compat.cpp
endif
-if ENABLE_TESTS
-libbitcoin_server_a_SOURCES += rpc/testtransactions.cpp
-endif
-
# cli
libbitcoin_cli_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
@@ -723,7 +704,7 @@ endif
$(AM_V_GEN) $(PROTOC) --cpp_out=$(@D) --proto_path=$(abspath $(= MAC_OS_VERSION_11_0
- if (__builtin_available(macOS 11.0, *)) {
+ //if (__builtin_available(macOS 11.0, *)) {
pthread_jit_write_protect_np(0);
- }
+ //}
#endif
#endif
return mem;
@@ -173,11 +173,11 @@ void setPagesRW(void* ptr, size_t bytes) {
char *errfunc;
#if defined(USE_PTHREAD_JIT_WP) && defined(MAC_OS_VERSION_11_0) \
&& MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_11_0
- if (__builtin_available(macOS 11.0, *)) {
+ //if (__builtin_available(macOS 11.0, *)) {
pthread_jit_write_protect_np(0);
- } else {
- pageProtect(ptr, bytes, PAGE_READWRITE, &errfunc);
- }
+ //} else {
+ // pageProtect(ptr, bytes, PAGE_READWRITE, &errfunc);
+ //}
#else
pageProtect(ptr, bytes, PAGE_READWRITE, &errfunc);
#endif
@@ -187,12 +187,12 @@ void setPagesRX(void* ptr, size_t bytes) {
char *errfunc;
#if defined(USE_PTHREAD_JIT_WP) && defined(MAC_OS_VERSION_11_0) \
&& MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_11_0
- if (__builtin_available(macOS 11.0, *)) {
+ //if (__builtin_available(macOS 11.0, *)) {
pthread_jit_write_protect_np(1);
__builtin___clear_cache((char*)ptr, ((char*)ptr) + bytes);
- } else {
- pageProtect(ptr, bytes, PAGE_EXECUTE_READ, &errfunc);
- }
+ //} else {
+ // pageProtect(ptr, bytes, PAGE_EXECUTE_READ, &errfunc);
+ //}
#else
pageProtect(ptr, bytes, PAGE_EXECUTE_READ, &errfunc);
#endif
diff --git a/src/cc/CCGateways.h b/src/cc/CCGateways.h
deleted file mode 100644
index 63d46ce8b..000000000
--- a/src/cc/CCGateways.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (c) 2016-2023 The Hush developers
-// Distributed under the GPLv3 software license, see the accompanying
-// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
-/******************************************************************************
- * Copyright © 2014-2019 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"
-
-bool GatewaysValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn);
-UniValue GatewaysBind(const CPubKey& pk, uint64_t txfee,std::string coin,uint256 tokenid,int64_t totalsupply,uint256 oracletxid,uint8_t M,uint8_t N,std::vector pubkeys,uint8_t p1,uint8_t p2,uint8_t p3,uint8_t p4);
-UniValue GatewaysDeposit(const CPubKey& pk, uint64_t txfee,uint256 bindtxid,int32_t height,std::string refcoin,uint256 cointxid,int32_t claimvout,std::string deposithex,std::vectorproof,CPubKey destpub,int64_t amount);
-UniValue GatewaysClaim(const CPubKey& pk, uint64_t txfee,uint256 bindtxid,std::string refcoin,uint256 deposittxid,CPubKey destpub,int64_t amount);
-UniValue GatewaysWithdraw(const CPubKey& pk, uint64_t txfee,uint256 bindtxid,std::string refcoin,CPubKey withdrawpub,int64_t amount);
-UniValue GatewaysPartialSign(const CPubKey& pk, uint64_t txfee,uint256 txidaddr,std::string refcoin,std::string hex);
-UniValue GatewaysCompleteSigning(const CPubKey& pk, uint64_t txfee,uint256 txidaddr,std::string refcoin,std::string hex);
-UniValue GatewaysMarkDone(const CPubKey& pk, uint64_t txfee,uint256 withdrawtxid,std::string refcoin);
-UniValue GatewaysPendingDeposits(const CPubKey& pk, uint256 bindtxid,std::string refcoin);
-UniValue GatewaysPendingWithdraws(const CPubKey& pk, uint256 bindtxid,std::string refcoin);
-UniValue GatewaysProcessedWithdraws(const CPubKey& pk, uint256 bindtxid,std::string refcoin);
-
-// CCcustom
-UniValue GatewaysInfo(uint256 bindtxid);
-UniValue GatewaysExternalAddress(uint256 bindtxid,CPubKey pubkey);
-UniValue GatewaysDumpPrivKey(uint256 bindtxid,CKey privkey);
-UniValue GatewaysList();
-
-#endif
diff --git a/src/cc/CCPayments.h b/src/cc/CCPayments.h
deleted file mode 100644
index 246a980d1..000000000
--- a/src/cc/CCPayments.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (c) 2016-2023 The Hush developers
-// Distributed under the GPLv3 software license, see the accompanying
-// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
-/******************************************************************************
- * Copyright © 2014-2019 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"
-#include
-#include
-
-#define PAYMENTS_TXFEE 10000
-#define PAYMENTS_MERGEOFSET 60 // 1H extra.
-extern std::vector > vAddressSnapshot;
-extern int32_t lastSnapShotHeight;
-
-bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn);
-
-// CCcustom
-UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr);
-UniValue PaymentsFund(struct CCcontract_info *cp,char *jsonstr);
-UniValue PaymentsMerge(struct CCcontract_info *cp,char *jsonstr);
-UniValue PaymentsTxidopret(struct CCcontract_info *cp,char *jsonstr);
-UniValue PaymentsCreate(struct CCcontract_info *cp,char *jsonstr);
-UniValue PaymentsAirdrop(struct CCcontract_info *cp,char *jsonstr);
-UniValue PaymentsAirdropTokens(struct CCcontract_info *cp,char *jsonstr);
-UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr);
-UniValue PaymentsList(struct CCcontract_info *cp,char *jsonstr);
-
-#endif
-
diff --git a/src/cc/CCPegs.h b/src/cc/CCPegs.h
index 197af430f..5cc2ce473 100644
--- a/src/cc/CCPegs.h
+++ b/src/cc/CCPegs.h
@@ -16,24 +16,3 @@
* *
******************************************************************************/
-
-#ifndef CC_PEGS_H
-#define CC_PEGS_H
-
-#include "CCinclude.h"
-
-bool PegsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn);
-
-// CCcustom
-UniValue PegsCreate(const CPubKey& pk,uint64_t txfee,int64_t amount,std::vector bindtxids);
-UniValue PegsFund(const CPubKey& pk,uint64_t txfee,uint256 pegstxid, uint256 tokenid, int64_t amount);
-UniValue PegsGet(const CPubKey& pk,uint64_t txfee,uint256 pegstxid, uint256 tokenid, int64_t amount);
-UniValue PegsRedeem(const CPubKey& pk,uint64_t txfee,uint256 pegstxid, uint256 tokenid);
-UniValue PegsLiquidate(const CPubKey& pk,uint64_t txfee,uint256 pegstxid, uint256 tokenid, uint256 liquidatetxid);
-UniValue PegsExchange(const CPubKey& pk,uint64_t txfee,uint256 pegstxid, uint256 tokenid, int64_t amount);
-UniValue PegsAccountHistory(const CPubKey& pk,uint256 pegstxid);
-UniValue PegsAccountInfo(const CPubKey& pk,uint256 pegstxid);
-UniValue PegsWorstAccounts(uint256 pegstxid);
-UniValue PegsInfo(uint256 pegstxid);
-
-#endif
diff --git a/src/cc/CCchannels.h b/src/cc/CCchannels.h
deleted file mode 100644
index e1fca87e8..000000000
--- a/src/cc/CCchannels.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (c) 2016-2023 The Hush developers
-// Distributed under the GPLv3 software license, see the accompanying
-// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
-/******************************************************************************
- * Copyright © 2014-2019 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, uint32_t nIn);
-UniValue ChannelOpen(const CPubKey& pk,uint64_t txfee,CPubKey destpub,int32_t numpayments,int64_t payment,uint256 tokenid);
-UniValue ChannelPayment(const CPubKey& pk,uint64_t txfee,uint256 opentxid,int64_t amount, uint256 secret);
-UniValue ChannelClose(const CPubKey& pk,uint64_t txfee,uint256 opentxid);
-UniValue ChannelRefund(const CPubKey& pk,uint64_t txfee,uint256 opentxid,uint256 closetxid);
-UniValue ChannelsList(const CPubKey& pk);
-// CCcustom
-UniValue ChannelsInfo(const CPubKey& pk,uint256 opentxid);
-
-#endif
diff --git a/src/cc/CCcustom.cpp b/src/cc/CCcustom.cpp
index 005fb1def..28e15b148 100644
--- a/src/cc/CCcustom.cpp
+++ b/src/cc/CCcustom.cpp
@@ -20,20 +20,13 @@
#include "CCinclude.h"
#include "CCassets.h"
#include "CCfaucet.h"
-#include "CCrewards.h"
-#include "CCdice.h"
#include "CCauction.h"
-#include "CClotto.h"
#include "CCfsm.h"
#include "CCHeir.h"
-#include "CCchannels.h"
#include "CCOracles.h"
#include "CCPrices.h"
#include "CCPegs.h"
-#include "CCPayments.h"
-#include "CCGateways.h"
#include "CCtokens.h"
-#include "CCImportGateway.h"
/*
CCcustom has most of the functions that need to be extended to create a new CC contract.
@@ -318,24 +311,24 @@ struct CCcontract_info *CCinit(struct CCcontract_info *cp, uint8_t evalcode)
strcpy(cp->normaladdr,RewardsNormaladdr);
strcpy(cp->CChexstr,RewardsCChexstr);
memcpy(cp->CCpriv,RewardsCCpriv,32);
- cp->validate = RewardsValidate;
- cp->ismyvin = IsRewardsInput;
+ //cp->validate = RewardsValidate;
+ //cp->ismyvin = IsRewardsInput;
break;
case EVAL_DICE:
strcpy(cp->unspendableCCaddr,DiceCCaddr);
strcpy(cp->normaladdr,DiceNormaladdr);
strcpy(cp->CChexstr,DiceCChexstr);
memcpy(cp->CCpriv,DiceCCpriv,32);
- cp->validate = DiceValidate;
- cp->ismyvin = IsDiceInput;
+ //cp->validate = DiceValidate;
+ //cp->ismyvin = IsDiceInput;
break;
case EVAL_LOTTO:
strcpy(cp->unspendableCCaddr,LottoCCaddr);
strcpy(cp->normaladdr,LottoNormaladdr);
strcpy(cp->CChexstr,LottoCChexstr);
memcpy(cp->CCpriv,LottoCCpriv,32);
- cp->validate = LottoValidate;
- cp->ismyvin = IsLottoInput;
+ //cp->validate = LottoValidate;
+ //cp->ismyvin = IsLottoInput;
break;
case EVAL_FSM:
strcpy(cp->unspendableCCaddr,FSMCCaddr);
@@ -366,8 +359,8 @@ struct CCcontract_info *CCinit(struct CCcontract_info *cp, uint8_t evalcode)
strcpy(cp->normaladdr,ChannelsNormaladdr);
strcpy(cp->CChexstr,ChannelsCChexstr);
memcpy(cp->CCpriv,ChannelsCCpriv,32);
- cp->validate = ChannelsValidate;
- cp->ismyvin = IsChannelsInput;
+ //cp->validate = ChannelsValidate;
+ //cp->ismyvin = IsChannelsInput;
break;
case EVAL_ORACLES:
strcpy(cp->unspendableCCaddr,OraclesCCaddr);
@@ -382,32 +375,32 @@ struct CCcontract_info *CCinit(struct CCcontract_info *cp, uint8_t evalcode)
strcpy(cp->normaladdr,PricesNormaladdr);
strcpy(cp->CChexstr,PricesCChexstr);
memcpy(cp->CCpriv,PricesCCpriv,32);
- cp->validate = PricesValidate;
- cp->ismyvin = IsPricesInput;
+ //cp->validate = PricesValidate;
+ //cp->ismyvin = IsPricesInput;
break;
case EVAL_PEGS:
strcpy(cp->unspendableCCaddr,PegsCCaddr);
strcpy(cp->normaladdr,PegsNormaladdr);
strcpy(cp->CChexstr,PegsCChexstr);
memcpy(cp->CCpriv,PegsCCpriv,32);
- cp->validate = PegsValidate;
- cp->ismyvin = IsPegsInput;
+ //cp->validate = PegsValidate;
+ //cp->ismyvin = IsPegsInput;
break;
case EVAL_PAYMENTS:
strcpy(cp->unspendableCCaddr,PaymentsCCaddr);
strcpy(cp->normaladdr,PaymentsNormaladdr);
strcpy(cp->CChexstr,PaymentsCChexstr);
memcpy(cp->CCpriv,PaymentsCCpriv,32);
- cp->validate = PaymentsValidate;
- cp->ismyvin = IsPaymentsInput;
+ //cp->validate = PaymentsValidate;
+ //cp->ismyvin = IsPaymentsInput;
break;
case EVAL_GATEWAYS:
strcpy(cp->unspendableCCaddr,GatewaysCCaddr);
strcpy(cp->normaladdr,GatewaysNormaladdr);
strcpy(cp->CChexstr,GatewaysCChexstr);
memcpy(cp->CCpriv,GatewaysCCpriv,32);
- cp->validate = GatewaysValidate;
- cp->ismyvin = IsGatewaysInput;
+ //cp->validate = GatewaysValidate;
+ //cp->ismyvin = IsGatewaysInput;
break;
case EVAL_TOKENS:
@@ -415,16 +408,16 @@ struct CCcontract_info *CCinit(struct CCcontract_info *cp, uint8_t evalcode)
strcpy(cp->normaladdr, TokensNormaladdr);
strcpy(cp->CChexstr, TokensCChexstr);
memcpy(cp->CCpriv, TokensCCpriv, 32);
- cp->validate = TokensValidate;
- cp->ismyvin = IsTokensInput;
+ //cp->validate = TokensValidate;
+ //cp->ismyvin = IsTokensInput;
break;
case EVAL_IMPORTGATEWAY:
strcpy(cp->unspendableCCaddr, ImportGatewayCCaddr);
strcpy(cp->normaladdr, ImportGatewayNormaladdr);
strcpy(cp->CChexstr, ImportGatewayCChexstr);
memcpy(cp->CCpriv, ImportGatewayCCpriv, 32);
- cp->validate = ImportGatewayValidate;
- cp->ismyvin = IsImportGatewayInput;
+ //cp->validate = ImportGatewayValidate;
+ //cp->ismyvin = IsImportGatewayInput;
break;
default:
if ( CClib_initcp(cp,evalcode) < 0 )
diff --git a/src/cc/CCdice.h b/src/cc/CCdice.h
deleted file mode 100644
index b1401ad2a..000000000
--- a/src/cc/CCdice.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (c) 2016-2023 The Hush developers
-// Distributed under the GPLv3 software license, see the accompanying
-// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
-/******************************************************************************
- * Copyright © 2014-2019 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_DICE_H
-#define CC_DICE_H
-
-#include "CCinclude.h"
-
-#define EVAL_DICE 0xe6
-
-bool DiceValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn);
-
-std::string DiceBet(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t bet,int32_t odds);
-std::string DiceBetFinish(uint8_t &funcid,uint256 &entropyused,int32_t &entropyvout,int32_t *resultp,uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 bettxid,int32_t winlosetimeout,uint256 vin0txid,int32_t vin0vout);
-double DiceStatus(uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 bettxid);
-std::string DiceCreateFunding(uint64_t txfee,char *planstr,int64_t funds,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks);
-std::string DiceAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t amount);
-UniValue DiceInfo(uint256 diceid);
-UniValue DiceList();
-int64_t DicePlanFunds(uint64_t &entropyval,uint256 &entropytxid,uint64_t refsbits,struct CCcontract_info *cp,CPubKey dicepk,uint256 reffundingtxid, int32_t &entropytxs,bool random);
-
-#endif
diff --git a/src/cc/CClotto.h b/src/cc/CClotto.h
deleted file mode 100644
index ad9c7131a..000000000
--- a/src/cc/CClotto.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (c) 2016-2023 The Hush developers
-// Distributed under the GPLv3 software license, see the accompanying
-// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
-/******************************************************************************
- * Copyright © 2014-2019 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_LOTTO_H
-#define CC_LOTTO_H
-
-#include "CCinclude.h"
-
-#define EVAL_LOTTO 0xe9
-
-bool LottoValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn);
-
-UniValue LottoInfo(uint256 lottoid);
-UniValue LottoList();
-std::string LottoTicket(uint64_t txfee,int64_t numtickets);
-std::string LottoWinner(uint64_t txfee);
-
-#endif
diff --git a/src/cc/CCrewards.h b/src/cc/CCrewards.h
deleted file mode 100644
index e78e20cdd..000000000
--- a/src/cc/CCrewards.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (c) 2016-2023 The Hush developers
-// Distributed under the GPLv3 software license, see the accompanying
-// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
-/******************************************************************************
- * Copyright © 2014-2019 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_REWARDS_H
-#define CC_REWARDS_H
-
-#include "CCinclude.h"
-#include
-
-#define EVAL_REWARDS 0xe5
-#define REWARDSCC_MAXAPR (COIN * 25)
-
-bool RewardsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn);
-UniValue RewardsInfo(uint256 rewardid);
-UniValue RewardsList();
-
-std::string RewardsCreateFunding(uint64_t txfee,char *planstr,int64_t funds,int64_t APR,int64_t minseconds,int64_t maxseconds,int64_t mindeposit);
-std::string RewardsAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t amount);
-std::string RewardsLock(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t amount);
-std::string RewardsUnlock(uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 locktxid);
-
-#endif
diff --git a/src/cc/CCtokens.cpp b/src/cc/CCtokens.cpp
deleted file mode 100644
index 236b4c3ed..000000000
--- a/src/cc/CCtokens.cpp
+++ /dev/null
@@ -1,1059 +0,0 @@
-// Copyright (c) 2016-2023 The Hush developers
-// Distributed under the GPLv3 software license, see the accompanying
-// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
-/******************************************************************************
- * Copyright © 2014-2019 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 "CCtokens.h"
-#include "importcoin.h"
-
-/* TODO: correct this:
------------------------------
- The SetTokenFillamounts() and ValidateTokenRemainder() work in tandem to calculate the vouts for a fill and to validate the vouts, respectively.
-
- This pair of functions are critical to make sure the trading is correct and is the trickiest part of the tokens contract.
-
- //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 tokenoshis to original pubkey
- //vout.3: CC output for tokenoshis change (if any)
- //vout.4: normal output for change (if any)
- //vout.n-1: opreturn [EVAL_ASSETS] ['B'] [tokenid] [remaining token required] [origpubkey]
- ValidateTokenRemainder(remaining_price,tx.vout[0].nValue,nValue,tx.vout[1].nValue,tx.vout[2].nValue,totalunits);
-
- Yes, this is quite confusing...
-
- In ValidateTokenRemainder the naming convention is nValue is the coin/token with the offer on the books and "units" is what it is being paid in. The high level check is to make sure we didnt lose any coins or tokens, the harder to validate is the actual price paid as the "orderbook" is in terms of the combined nValue for the combined totalunits.
-
- We assume that the effective unit cost in the orderbook is valid and that that amount was paid and also that any remainder will be close enough in effective unit cost to not matter. At the edge cases, this will probably be not true and maybe some orders wont be practically fillable when reduced to fractional state. However, the original pubkey that created the offer can always reclaim it.
- ------------------------------
-*/
-
-
-
-// tx validation
-bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
-{
- static uint256 zero;
- CTxDestination address; CTransaction vinTx, createTx; uint256 hashBlock, tokenid, tokenid2;
- int32_t i, starti, numvins, numvouts, preventCCvins, preventCCvouts;
- int64_t remaining_price, nValue, tokenoshis, outputs, inputs, tmpprice, totalunits, ignore;
- std::vector> oprets;
- vscript_t /*vopretExtra,*/ tmporigpubkey, ignorepubkey;
- uint8_t funcid, evalCodeInOpret;
- char destaddr[64], origaddr[64], CCaddr[64];
- std::vector voutTokenPubkeys, vinTokenPubkeys;
-
- if (strcmp(SMART_CHAIN_SYMBOL, "ROGUE") == 0 && chainActive.Height() <= 12500)
- return true;
-
- numvins = tx.vin.size();
- numvouts = tx.vout.size();
- outputs = inputs = 0;
- preventCCvins = preventCCvouts = -1;
-
- // check boundaries:
- if (numvouts < 1)
- return eval->Invalid("no vouts");
-
- if ((funcid = DecodeTokenOpRet(tx.vout[numvouts - 1].scriptPubKey, evalCodeInOpret, tokenid, voutTokenPubkeys, oprets)) == 0)
- return eval->Invalid("TokenValidate: invalid opreturn payload");
-
- LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "TokensValidate funcId=" << (char)(funcid?funcid:' ') << " evalcode=" << std::hex << (int)cp->evalcode << std::endl);
-
- if (eval->GetTxUnconfirmed(tokenid, createTx, hashBlock) == 0)
- return eval->Invalid("cant find token create txid");
- //else if (IsCCInput(tx.vin[0].scriptSig) != 0)
- // return eval->Invalid("illegal token vin0"); // <-- this validation was removed because some token tx might not have normal vins
- else if (funcid != 'c')
- {
- if (tokenid == zeroid)
- return eval->Invalid("illegal tokenid");
- else if (!TokensExactAmounts(true, cp, inputs, outputs, eval, tx, tokenid)) {
- if (!eval->Valid())
- return false; //TokenExactAmounts must call eval->Invalid()!
- else
- return eval->Invalid("tokens cc inputs != cc outputs");
- }
- }
-
- // validate spending from token cc addr: allowed only for burned non-fungible tokens:
- if (ExtractTokensCCVinPubkeys(tx, vinTokenPubkeys) && std::find(vinTokenPubkeys.begin(), vinTokenPubkeys.end(), GetUnspendable(cp, NULL)) != vinTokenPubkeys.end()) {
- // validate spending from token unspendable cc addr:
- int64_t burnedAmount = HasBurnedTokensvouts(cp, eval, tx, tokenid);
- if (burnedAmount > 0) {
- vscript_t vopretNonfungible;
- GetNonfungibleData(tokenid, vopretNonfungible);
- if( vopretNonfungible.empty() )
- return eval->Invalid("spending cc marker not supported for fungible tokens");
- }
- }
-
- switch (funcid)
- {
- case 'c': // token create should not be validated as it has no CC inputs, so return 'invalid'
- // token tx structure for 'c':
- //vin.0: normal input
- //vout.0: issuance tokenoshis to CC
- //vout.1: normal output for change (if any)
- //vout.n-1: opreturn EVAL_TOKENS 'c'
- return eval->Invalid("incorrect token funcid");
-
- case 't': // transfer
- // token tx structure for 't'
- //vin.0: normal input
- //vin.1 .. vin.n-1: valid CC outputs
- //vout.0 to n-2: tokenoshis output to CC
- //vout.n-2: normal output for change (if any)
- //vout.n-1: opreturn EVAL_TOKENS 't' tokenid
- if (inputs == 0)
- return eval->Invalid("no token inputs for transfer");
-
- LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "token transfer preliminarily validated inputs=" << inputs << "->outputs=" << outputs << " preventCCvins=" << preventCCvins<< " preventCCvouts=" << preventCCvouts << std::endl);
- break; // breaking to other contract validation...
-
- default:
- LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "illegal tokens funcid=" << (char)(funcid?funcid:' ') << std::endl);
- return eval->Invalid("unexpected token funcid");
- }
-
- return true;
-}
-
-// helper funcs:
-
-// extract cc token vins' pubkeys:
-bool ExtractTokensCCVinPubkeys(const CTransaction &tx, std::vector &vinPubkeys) {
-
- bool found = false;
- CPubKey pubkey;
- struct CCcontract_info *cpTokens, tokensC;
-
- cpTokens = CCinit(&tokensC, EVAL_TOKENS);
- vinPubkeys.clear();
-
- for (int32_t i = 0; i < tx.vin.size(); i++)
- {
- // check for cc token vins:
- if( (*cpTokens->ismyvin)(tx.vin[i].scriptSig) )
- {
-
- auto findEval = [](CC *cond, struct CCVisitor _) {
- bool r = false;
-
- if (cc_typeId(cond) == CC_Secp256k1) {
- *(CPubKey*)_.context = buf2pk(cond->publicKey);
- //std::cerr << "findEval found pubkey=" << HexStr(*(CPubKey*)_.context) << std::endl;
- r = true;
- }
- // false for a match, true for continue
- return r ? 0 : 1;
- };
-
- CC *cond = GetCryptoCondition(tx.vin[i].scriptSig);
-
- if (cond) {
- CCVisitor visitor = { findEval, (uint8_t*)"", 0, &pubkey };
- bool out = !cc_visit(cond, visitor);
- cc_free(cond);
-
- if (pubkey.IsValid()) {
- vinPubkeys.push_back(pubkey);
- found = true;
- }
- }
- }
- }
- return found;
-}
-
-// this is just for log messages indentation fur debugging recursive calls:
-thread_local uint32_t tokenValIndentSize = 0;
-
-// validates opret for token tx:
-uint8_t ValidateTokenOpret(CTransaction tx, uint256 tokenid) {
-
- uint256 tokenidOpret = zeroid;
- uint8_t funcid;
- uint8_t dummyEvalCode;
- std::vector voutPubkeysDummy;
- std::vector> opretsDummy;
-
- // this is just for log messages indentation fur debugging recursive calls:
- std::string indentStr = std::string().append(tokenValIndentSize, '.');
-
- if (tx.vout.size() == 0)
- return (uint8_t)0;
-
- if ((funcid = DecodeTokenOpRet(tx.vout.back().scriptPubKey, dummyEvalCode, tokenidOpret, voutPubkeysDummy, opretsDummy)) == 0)
- {
- LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << indentStr << "ValidateTokenOpret() DecodeTokenOpret could not parse opret for txid=" << tx.GetHash().GetHex() << std::endl);
- return (uint8_t)0;
- }
- else if (funcid == 'c')
- {
- if (tokenid != zeroid && tokenid == tx.GetHash()) {
- LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() this is tokenbase 'c' tx, txid=" << tx.GetHash().GetHex() << " returning true" << std::endl);
- return funcid;
- }
- else {
- LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() not my tokenbase txid=" << tx.GetHash().GetHex() << std::endl);
- }
- }
- else if (funcid == 'i')
- {
- if (tokenid != zeroid && tokenid == tx.GetHash()) {
- LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() this is import 'i' tx, txid=" << tx.GetHash().GetHex() << " returning true" << std::endl);
- return funcid;
- }
- else {
- LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() not my import txid=" << tx.GetHash().GetHex() << std::endl);
- }
- }
- else if (funcid == 't')
- {
- //std::cerr << indentStr << "ValidateTokenOpret() tokenid=" << tokenid.GetHex() << " tokenIdOpret=" << tokenidOpret.GetHex() << " txid=" << tx.GetHash().GetHex() << std::endl;
- if (tokenid != zeroid && tokenid == tokenidOpret) {
- LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() this is a transfer 't' tx, txid=" << tx.GetHash().GetHex() << " returning true" << std::endl);
- return funcid;
- }
- else {
- LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() not my tokenid=" << tokenidOpret.GetHex() << std::endl);
- }
- }
- else {
- LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() not supported funcid=" << (char)funcid << " tokenIdOpret=" << tokenidOpret.GetHex() << " txid=" << tx.GetHash().GetHex() << std::endl);
- }
- return (uint8_t)0;
-}
-
-// remove token->unspendablePk (it is only for marker usage)
-void FilterOutTokensUnspendablePk(const std::vector &sourcePubkeys, std::vector &destPubkeys) {
- struct CCcontract_info *cpTokens, tokensC;
- cpTokens = CCinit(&tokensC, EVAL_TOKENS);
- CPubKey tokensUnspendablePk = GetUnspendable(cpTokens, NULL);
- destPubkeys.clear();
-
- for (auto pk : sourcePubkeys)
- if (pk != tokensUnspendablePk)
- destPubkeys.push_back(pk);
-
-}
-
-void FilterOutNonCCOprets(const std::vector> &oprets, vscript_t &vopret) {
-
- vopret.clear();
-
- if (oprets.size() > 2)
- LOGSTREAM("cctokens", CCLOG_INFO, stream << "FilterOutNonCCOprets() warning!! oprets.size > 2 currently not supported" << oprets.size() << std::endl);
-
- for (auto o : oprets) {
- if (o.first < OPRETID_FIRSTNONCCDATA) { // skip burn, import, etc opret data
- vopret = o.second; // return first contract opret (more than 1 is not supported yet)
- break;
- }
- }
-}
-
-// Checks if the vout is a really Tokens CC vout
-// also checks tokenid in opret or txid if this is 'c' tx
-// goDeeper is true: the func also validates amounts of the passed transaction:
-// it should be either sum(cc vins) == sum(cc vouts) or the transaction is the 'tokenbase' ('c') tx
-// checkPubkeys is true: validates if the vout is token vout1 or token vout1of2. Should always be true!
-int64_t IsTokensvout(bool goDeeper, bool checkPubkeys /*<--not used, always true*/, struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, int32_t v, uint256 reftokenid)
-{
-
- // this is just for log messages indentation fur debugging recursive calls:
- std::string indentStr = std::string().append(tokenValIndentSize, '.');
-
- LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << indentStr << "IsTokensvout() entered for txid=" << tx.GetHash().GetHex() << " v=" << v << " for tokenid=" << reftokenid.GetHex() << std::endl);
-
- int32_t n = tx.vout.size();
- // just check boundaries:
- if (n == 0 || v < 0 || v >= n-1) {
- LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << indentStr << "isTokensvout() incorrect params: (n == 0 or v < 0 or v >= n-1)" << " v=" << v << " n=" << n << " returning 0" << std::endl);
- return(0);
- }
-
- if (tx.vout[v].scriptPubKey.IsPayToCryptoCondition())
- {
- if (goDeeper) {
- //validate all tx
- int64_t myCCVinsAmount = 0, myCCVoutsAmount = 0;
-
- tokenValIndentSize++;
- // false --> because we already at the 1-st level ancestor tx and do not need to dereference ancestors of next levels
- bool isEqual = TokensExactAmounts(false, cp, myCCVinsAmount, myCCVoutsAmount, eval, tx, reftokenid);
- tokenValIndentSize--;
-
- if (!isEqual) {
- // if ccInputs != ccOutputs and it is not the tokenbase tx
- // this means it is possibly a fake tx (dimxy):
- if (reftokenid != tx.GetHash()) { // checking that this is the true tokenbase tx, by verifying that funcid=c, is done further in this function (dimxy)
- LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << indentStr << "IsTokensvout() warning: for the verified tx detected a bad vintx=" << tx.GetHash().GetHex() << ": cc inputs != cc outputs and not the 'tokenbase' tx, skipping the verified tx" << std::endl);
- return 0;
- }
- }
- }
-
- // token opret most important checks (tokenid == reftokenid, tokenid is non-zero, tx is 'tokenbase'):
- const uint8_t funcId = ValidateTokenOpret(tx, reftokenid);
- //std::cerr << indentStr << "IsTokensvout() ValidateTokenOpret returned=" << (char)(funcId?funcId:' ') << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl;
- if (funcId != 0) {
- LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << indentStr << "IsTokensvout() ValidateTokenOpret returned not-null funcId=" << (char)(funcId ? funcId : ' ') << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl);
-
- uint8_t dummyEvalCode;
- uint256 tokenIdOpret;
- std::vector voutPubkeys, voutPubkeysInOpret;
- vscript_t vopretExtra, vopretNonfungible;
- std::vector> oprets;
-
- uint8_t evalCodeNonfungible = 0;
- uint8_t evalCode1 = EVAL_TOKENS; // if both payloads are empty maybe it is a transfer to non-payload-one-eval-token vout like GatewaysClaim
- uint8_t evalCode2 = 0; // will be checked if zero or not
-
- // test vouts for possible token use-cases:
- std::vector> testVouts;
-
- DecodeTokenOpRet(tx.vout.back().scriptPubKey, dummyEvalCode, tokenIdOpret, voutPubkeysInOpret, oprets);
- LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << "IsTokensvout() oprets.size()=" << oprets.size() << std::endl);
-
- // get assets/channels/gateways token data:
- FilterOutNonCCOprets(oprets, vopretExtra); // NOTE: only 1 additional evalcode in token opret is currently supported
- LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << "IsTokensvout() vopretExtra=" << HexStr(vopretExtra) << std::endl);
-
- // get non-fungible data
- GetNonfungibleData(reftokenid, vopretNonfungible);
- FilterOutTokensUnspendablePk(voutPubkeysInOpret, voutPubkeys); // cannot send tokens to token unspendable cc addr (only marker is allowed there)
-
- // NOTE: evalcode order in vouts is important:
- // non-fungible-eval -> EVAL_TOKENS -> assets-eval
-
- if (vopretNonfungible.size() > 0)
- evalCodeNonfungible = evalCode1 = vopretNonfungible.begin()[0];
- if (vopretExtra.size() > 0)
- evalCode2 = vopretExtra.begin()[0];
-
- if (evalCode1 == EVAL_TOKENS && evalCode2 != 0) {
- evalCode1 = evalCode2; // for using MakeTokensCC1vout(evalcode,...) instead of MakeCC1vout(EVAL_TOKENS, evalcode...)
- evalCode2 = 0;
- }
-
- if( /*checkPubkeys &&*/ funcId != 'c' ) { // for 'c' there is no pubkeys
- // verify that the vout is token by constructing vouts with the pubkeys in the opret:
-
- // maybe this is dual-eval 1 pubkey or 1of2 pubkey vout?
- if (voutPubkeys.size() >= 1 && voutPubkeys.size() <= 2) {
- // check dual/three-eval 1 pubkey vout with the first pubkey
- testVouts.push_back( std::make_pair(MakeTokensCC1vout(evalCode1, evalCode2, tx.vout[v].nValue, voutPubkeys[0]), std::string("three-eval cc1 pk[0]")) );
- if (evalCode2 != 0)
- // also check in backward evalcode order
- testVouts.push_back( std::make_pair(MakeTokensCC1vout(evalCode2, evalCode1, tx.vout[v].nValue, voutPubkeys[0]), std::string("three-eval cc1 pk[0] backward-eval")) );
-
- if(voutPubkeys.size() == 2) {
- // check dual/three eval 1of2 pubkeys vout
- testVouts.push_back( std::make_pair(MakeTokensCC1of2vout(evalCode1, evalCode2, tx.vout[v].nValue, voutPubkeys[0], voutPubkeys[1]), std::string("three-eval cc1of2")) );
- // check dual/three eval 1 pubkey vout with the second pubkey
- testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode1, evalCode2, tx.vout[v].nValue, voutPubkeys[1]), std::string("three-eval cc1 pk[1]")));
- if (evalCode2 != 0) {
- // also check in backward evalcode order:
- // check dual/three eval 1of2 pubkeys vout
- testVouts.push_back(std::make_pair(MakeTokensCC1of2vout(evalCode2, evalCode1, tx.vout[v].nValue, voutPubkeys[0], voutPubkeys[1]), std::string("three-eval cc1of2 backward-eval")));
- // check dual/three eval 1 pubkey vout with the second pubkey
- testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, evalCode1, tx.vout[v].nValue, voutPubkeys[1]), std::string("three-eval cc1 pk[1] backward-eval")));
- }
- }
-
- // maybe this is like gatewayclaim to single-eval token?
- if( evalCodeNonfungible == 0 ) // do not allow to convert non-fungible to fungible token
- testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, voutPubkeys[0]), std::string("single-eval cc1 pk[0]")));
-
- // maybe this is like FillSell for non-fungible token?
- if( evalCode1 != 0 )
- testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode1, tx.vout[v].nValue, voutPubkeys[0]), std::string("dual-eval-token cc1 pk[0]")));
- if( evalCode2 != 0 )
- testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, tx.vout[v].nValue, voutPubkeys[0]), std::string("dual-eval2-token cc1 pk[0]")));
-
- // the same for pk[1]:
- if (voutPubkeys.size() == 2) {
- if (evalCodeNonfungible == 0) // do not allow to convert non-fungible to fungible token
- testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, voutPubkeys[1]), std::string("single-eval cc1 pk[1]")));
- if (evalCode1 != 0)
- testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode1, tx.vout[v].nValue, voutPubkeys[1]), std::string("dual-eval-token cc1 pk[1]")));
- if (evalCode2 != 0)
- testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, tx.vout[v].nValue, voutPubkeys[1]), std::string("dual-eval2-token cc1 pk[1]")));
- }
- }
-
- //special check for tx when spending from 1of2 CC address and one of pubkeys is global CC pubkey
- struct CCcontract_info *cpEvalCode1,CEvalCode1;
- cpEvalCode1 = CCinit(&CEvalCode1,evalCode1);
- CPubKey pk=GetUnspendable(cpEvalCode1,0);
- testVouts.push_back( std::make_pair(MakeTokensCC1of2vout(evalCode1, tx.vout[v].nValue, voutPubkeys[0], pk), std::string("dual-eval1 pegscc cc1of2 pk[0] globalccpk")) );
- if (voutPubkeys.size() == 2) testVouts.push_back( std::make_pair(MakeTokensCC1of2vout(evalCode1, tx.vout[v].nValue, voutPubkeys[1], pk), std::string("dual-eval1 pegscc cc1of2 pk[1] globalccpk")) );
- if (evalCode2!=0)
- {
- struct CCcontract_info *cpEvalCode2,CEvalCode2;
- cpEvalCode2 = CCinit(&CEvalCode2,evalCode2);
- CPubKey pk=GetUnspendable(cpEvalCode2,0);
- testVouts.push_back( std::make_pair(MakeTokensCC1of2vout(evalCode2, tx.vout[v].nValue, voutPubkeys[0], pk), std::string("dual-eval2 pegscc cc1of2 pk[0] globalccpk")) );
- if (voutPubkeys.size() == 2) testVouts.push_back( std::make_pair(MakeTokensCC1of2vout(evalCode2, tx.vout[v].nValue, voutPubkeys[1], pk), std::string("dual-eval2 pegscc cc1of2 pk[1] globalccpk")) );
- }
-
- // maybe it is single-eval or dual/three-eval token change?
- std::vector vinPubkeys, vinPubkeysUnfiltered;
- ExtractTokensCCVinPubkeys(tx, vinPubkeysUnfiltered);
- FilterOutTokensUnspendablePk(vinPubkeysUnfiltered, vinPubkeys); // cannot send tokens to token unspendable cc addr (only marker is allowed there)
-
- for(std::vector::iterator it = vinPubkeys.begin(); it != vinPubkeys.end(); it++) {
- if (evalCodeNonfungible == 0) // do not allow to convert non-fungible to fungible token
- testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, *it), std::string("single-eval cc1 self vin pk")));
- testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode1, evalCode2, tx.vout[v].nValue, *it), std::string("three-eval cc1 self vin pk")));
-
- if (evalCode2 != 0)
- // also check in backward evalcode order:
- testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, evalCode1, tx.vout[v].nValue, *it), std::string("three-eval cc1 self vin pk backward-eval")));
- }
-
- // try all test vouts:
- for (auto t : testVouts) {
- if (t.first == tx.vout[v]) { // test vout matches
- LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "IsTokensvout() valid amount=" << tx.vout[v].nValue << " msg=" << t.second << " evalCode=" << (int)evalCode1 << " evalCode2=" << (int)evalCode2 << " txid=" << tx.GetHash().GetHex() << " tokenid=" << reftokenid.GetHex() << std::endl);
- return tx.vout[v].nValue;
- }
- }
-
- }
- else { // funcid == 'c'
-
- if (!tx.IsCoinImport()) {
-
- vscript_t vorigPubkey;
- std::string dummyName, dummyDescription;
- std::vector> oprets;
-
- if (DecodeTokenCreateOpRet(tx.vout.back().scriptPubKey, vorigPubkey, dummyName, dummyDescription, oprets) == 0) {
- LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << indentStr << "IsTokensvout() could not decode create opret" << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl);
- return 0;
- }
-
- CPubKey origPubkey = pubkey2pk(vorigPubkey);
-
-
- // TODO: add voutPubkeys for 'c' tx
-
- /* this would not work for imported tokens:
- // for 'c' recognize the tokens only to token originator pubkey (but not to unspendable <-- closed sec violation)
- // maybe this is like gatewayclaim to single-eval token?
- if (evalCodeNonfungible == 0) // do not allow to convert non-fungible to fungible token
- testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, origPubkey), std::string("single-eval cc1 orig-pk")));
- // maybe this is like FillSell for non-fungible token?
- if (evalCode1 != 0)
- testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode1, tx.vout[v].nValue, origPubkey), std::string("dual-eval-token cc1 orig-pk"))); */
-
- // note: this would not work if there are several pubkeys in the tokencreator's wallet (AddNormalinputs does not use pubkey param):
- // for tokenbase tx check that normal inputs sent from origpubkey > cc outputs
- int64_t ccOutputs = 0;
- for (auto vout : tx.vout)
- if (vout.scriptPubKey.IsPayToCryptoCondition() //TODO: add voutPubkey validation
- && !IsTokenMarkerVout(vout)) // should not be marker here
- ccOutputs += vout.nValue;
-
- int64_t normalInputs = TotalPubkeyNormalInputs(tx, origPubkey); // check if normal inputs are really signed by originator pubkey (someone not cheating with originator pubkey)
- LOGSTREAM("cctokens", CCLOG_DEBUG2, stream << indentStr << "IsTokensvout() normalInputs=" << normalInputs << " ccOutputs=" << ccOutputs << " for tokenbase=" << reftokenid.GetHex() << std::endl);
-
- if (normalInputs >= ccOutputs) {
- LOGSTREAM("cctokens", CCLOG_DEBUG2, stream << indentStr << "IsTokensvout() assured normalInputs >= ccOutputs" << " for tokenbase=" << reftokenid.GetHex() << std::endl);
- if (!IsTokenMarkerVout(tx.vout[v])) // exclude marker
- return tx.vout[v].nValue;
- else
- return 0; // vout is good, but do not take marker into account
- }
- else {
- LOGSTREAM("cctokens", CCLOG_INFO, stream << indentStr << "IsTokensvout() skipping vout not fulfilled normalInputs >= ccOutput" << " for tokenbase=" << reftokenid.GetHex() << " normalInputs=" << normalInputs << " ccOutputs=" << ccOutputs << std::endl);
- }
- }
- else {
- // imported tokens are checked in the eval::ImportCoin() validation code
- if (!IsTokenMarkerVout(tx.vout[v])) // exclude marker
- return tx.vout[v].nValue;
- else
- return 0; // vout is good, but do not take marker into account
- }
- }
- LOGSTREAM("cctokens", CCLOG_DEBUG1, stream << indentStr << "IsTokensvout() no valid vouts evalCode=" << (int)evalCode1 << " evalCode2=" << (int)evalCode2 << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl);
- }
- //std::cerr << indentStr; fprintf(stderr,"IsTokensvout() CC vout v.%d of n=%d amount=%.8f txid=%s\n",v,n,(double)0/COIN, tx.GetHash().GetHex().c_str());
- }
- //std::cerr << indentStr; fprintf(stderr,"IsTokensvout() normal output v.%d %.8f\n",v,(double)tx.vout[v].nValue/COIN);
- return(0);
-}
-
-bool IsTokenMarkerVout(CTxOut vout) {
- struct CCcontract_info *cpTokens, CCtokens_info;
- cpTokens = CCinit(&CCtokens_info, EVAL_TOKENS);
- return vout == MakeCC1vout(EVAL_TOKENS, vout.nValue, GetUnspendable(cpTokens, NULL));
-}
-
-// compares cc inputs vs cc outputs (to prevent feeding vouts from normal inputs)
-bool TokensExactAmounts(bool goDeeper, struct CCcontract_info *cp, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 reftokenid)
-{
- CTransaction vinTx;
- uint256 hashBlock;
- int64_t tokenoshis;
-
- struct CCcontract_info *cpTokens, tokensC;
- cpTokens = CCinit(&tokensC, EVAL_TOKENS);
-
- int32_t numvins = tx.vin.size();
- int32_t numvouts = tx.vout.size();
- inputs = outputs = 0;
-
- // this is just for log messages indentation for debugging recursive calls:
- std::string indentStr = std::string().append(tokenValIndentSize, '.');
-
- LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << indentStr << "TokensExactAmounts() entered for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl);
-
- for (int32_t i = 0; iismyvin)(tx.vin[i].scriptSig) /*|| IsVinAllowed(tx.vin[i].scriptSig) != 0*/)
- {
- //std::cerr << indentStr << "TokensExactAmounts() eval is true=" << (eval != NULL) << " ismyvin=ok for_i=" << i << std::endl;
- // we are not inside the validation code -- dimxy
- if ((eval && eval->GetTxUnconfirmed(tx.vin[i].prevout.hash, vinTx, hashBlock) == 0) || (!eval && !myGetTransaction(tx.vin[i].prevout.hash, vinTx, hashBlock)))
- {
- LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << indentStr << "TokensExactAmounts() cannot read vintx for i." << i << " numvins." << numvins << std::endl);
- return (!eval) ? false : eval->Invalid("always should find vin tx, but didnt");
- }
- else {
- LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << indentStr << "TokenExactAmounts() checking vintx.vout for tx.vin[" << i << "] nValue=" << vinTx.vout[tx.vin[i].prevout.n].nValue << std::endl);
-
- // validate vouts of vintx
- tokenValIndentSize++;
- tokenoshis = IsTokensvout(goDeeper, true, cpTokens, eval, vinTx, tx.vin[i].prevout.n, reftokenid);
- tokenValIndentSize--;
- if (tokenoshis != 0)
- {
- LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "TokensExactAmounts() adding vintx.vout for tx.vin[" << i << "] tokenoshis=" << tokenoshis << std::endl);
- inputs += tokenoshis;
- }
- }
- }
- }
-
- for (int32_t i = 0; i < numvouts-1; i ++) // 'numvouts-1' <-- do not check opret
- {
- LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << indentStr << "TokenExactAmounts() recursively checking tx.vout[" << i << "] nValue=" << tx.vout[i].nValue << std::endl);
-
- // Note: we pass in here IsTokenvout(false,...) because we don't need to call TokenExactAmounts() recursively from IsTokensvout here
- // indeed, if we pass 'true' we'll be checking this tx vout again
- tokenValIndentSize++;
- tokenoshis = IsTokensvout(false /*<--do not recursion here*/, true /*<--exclude non-tokens vouts*/, cpTokens, eval, tx, i, reftokenid);
- tokenValIndentSize--;
-
- if (tokenoshis != 0)
- {
- LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "TokensExactAmounts() adding tx.vout[" << i << "] tokenoshis=" << tokenoshis << std::endl);
- outputs += tokenoshis;
- }
- }
-
- //std::cerr << indentStr << "TokensExactAmounts() inputs=" << inputs << " outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << std::endl;
-
- if (inputs != outputs) {
- if (tx.GetHash() != reftokenid)
- LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "TokenExactAmounts() found unequal token cc inputs=" << inputs << " vs cc outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << " and this is not the create tx" << std::endl);
- //fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs);
- return false; // do not call eval->Invalid() here!
- }
- else
- return true;
-}
-
-
-// get non-fungible data from 'tokenbase' tx (the data might be empty)
-void GetNonfungibleData(uint256 tokenid, vscript_t &vopretNonfungible)
-{
- CTransaction tokenbasetx;
- uint256 hashBlock;
-
- if (!myGetTransaction(tokenid, tokenbasetx, hashBlock)) {
- LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "GetNonfungibleData() cound not load token creation tx=" << tokenid.GetHex() << std::endl);
- return;
- }
-
- vopretNonfungible.clear();
- // check if it is non-fungible tx and get its second evalcode from non-fungible payload
- if (tokenbasetx.vout.size() > 0) {
- std::vector origpubkey;
- std::string name, description;
- std::vector> oprets;
-
- if (DecodeTokenCreateOpRet(tokenbasetx.vout.back().scriptPubKey, origpubkey, name, description, oprets) == 'c') {
- GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vopretNonfungible);
- }
- }
-}
-
-
-// overload, adds inputs from token cc addr
-int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs) {
- vscript_t vopretNonfungibleDummy;
- return AddTokenCCInputs(cp, mtx, pk, tokenid, total, maxinputs, vopretNonfungibleDummy);
-}
-
-// adds inputs from token cc addr and returns non-fungible opret payload if present
-// also sets evalcode in cp, if needed
-int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs, vscript_t &vopretNonfungible)
-{
- char tokenaddr[64], destaddr[64];
- int64_t threshold, nValue, price, totalinputs = 0;
- int32_t n = 0;
- std::vector > unspentOutputs;
-
- GetNonfungibleData(tokenid, vopretNonfungible);
- if (vopretNonfungible.size() > 0)
- cp->additionalTokensEvalcode2 = vopretNonfungible.begin()[0];
-
- GetTokensCCaddress(cp, tokenaddr, pk);
- SetCCunspents(unspentOutputs, tokenaddr,true);
-
-
- if (unspentOutputs.empty()) {
- LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "AddTokenCCInputs() no utxos for token dual/three eval addr=" << tokenaddr << " evalcode=" << (int)cp->evalcode << " additionalTokensEvalcode2=" << (int)cp->additionalTokensEvalcode2 << std::endl);
- }
-
- threshold = total / (maxinputs != 0 ? maxinputs : CC_MAXVINS);
-
- for (std::vector >::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++)
- {
- CTransaction vintx;
- uint256 hashBlock;
- uint256 vintxid = it->first.txhash;
- int32_t vout = (int32_t)it->first.index;
-
- if (it->second.satoshis < threshold) // this should work also for non-fungible tokens (there should be only 1 satoshi for non-fungible token issue)
- continue;
-
- int32_t ivin;
- for (ivin = 0; ivin < mtx.vin.size(); ivin ++)
- if (vintxid == mtx.vin[ivin].prevout.hash && vout == mtx.vin[ivin].prevout.n)
- break;
- if (ivin != mtx.vin.size()) // that is, the tx.vout is already added to mtx.vin (in some previous calls)
- continue;
-
- if (myGetTransaction(vintxid, vintx, hashBlock) != 0)
- {
- Getscriptaddress(destaddr, vintx.vout[vout].scriptPubKey);
- if (strcmp(destaddr, tokenaddr) != 0 &&
- strcmp(destaddr, cp->unspendableCCaddr) != 0 && // TODO: check why this. Should not we add token inputs from unspendable cc addr if mypubkey is used?
- strcmp(destaddr, cp->unspendableaddr2) != 0) // or the logic is to allow to spend all available tokens (what about unspendableaddr3)?
- continue;
-
- LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << "AddTokenCCInputs() check vintx vout destaddress=" << destaddr << " amount=" << vintx.vout[vout].nValue << std::endl);
-
- if ((nValue = IsTokensvout(true, true/*<--add only valid token uxtos */, cp, NULL, vintx, vout, tokenid)) > 0 && myIsutxo_spentinmempool(ignoretxid,ignorevin,vintxid, vout) == 0)
- {
- //for non-fungible tokens check payload:
- if (!vopretNonfungible.empty()) {
- vscript_t vopret;
-
- // check if it is non-fungible token:
- GetNonfungibleData(tokenid, vopret);
- if (vopret != vopretNonfungible) {
- LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "AddTokenCCInputs() found incorrect non-fungible opret payload for vintxid=" << vintxid.GetHex() << std::endl);
- continue;
- }
- // non-fungible evalCode2 cc contract should also check if there exists only one non-fungible vout with amount = 1
- }
-
-
- if (total != 0 && maxinputs != 0) // if it is not just to calc amount...
- mtx.vin.push_back(CTxIn(vintxid, vout, CScript()));
-
- nValue = it->second.satoshis;
- totalinputs += nValue;
- LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << "AddTokenCCInputs() adding input nValue=" << nValue << std::endl);
- n++;
-
- if ((total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs))
- break;
- }
- }
- }
-
- //std::cerr << "AddTokenCCInputs() found totalinputs=" << totalinputs << std::endl;
- return(totalinputs);
-}
-
-// checks if any token vouts are sent to 'dead' pubkey
-int64_t HasBurnedTokensvouts(struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, uint256 reftokenid)
-{
- uint8_t dummyEvalCode;
- uint256 tokenIdOpret;
- std::vector voutPubkeys, voutPubkeysDummy;
- std::vector> oprets;
- vscript_t vopretExtra, vopretNonfungible;
-
- uint8_t evalCode = EVAL_TOKENS; // if both payloads are empty maybe it is a transfer to non-payload-one-eval-token vout like GatewaysClaim
- uint8_t evalCode2 = 0; // will be checked if zero or not
-
- // test vouts for possible token use-cases:
- std::vector> testVouts;
-
- int32_t n = tx.vout.size();
- // just check boundaries:
- if (n == 0) {
- LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "HasBurnedTokensvouts() incorrect params: tx.vout.size() == 0, txid=" << tx.GetHash().GetHex() << std::endl);
- return(0);
- }
-
-
- if (DecodeTokenOpRet(tx.vout.back().scriptPubKey, dummyEvalCode, tokenIdOpret, voutPubkeysDummy, oprets) == 0) {
- LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "HasBurnedTokensvouts() cannot parse opret DecodeTokenOpRet returned 0, txid=" << tx.GetHash().GetHex() << std::endl);
- return 0;
- }
-
- // get assets/channels/gateways token data:
- FilterOutNonCCOprets(oprets, vopretExtra); // NOTE: only 1 additional evalcode in token opret is currently supported
-
- LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << "HasBurnedTokensvouts() vopretExtra=" << HexStr(vopretExtra) << std::endl);
-
- GetNonfungibleData(reftokenid, vopretNonfungible);
-
- if (vopretNonfungible.size() > 0)
- evalCode = vopretNonfungible.begin()[0];
- if (vopretExtra.size() > 0)
- evalCode2 = vopretExtra.begin()[0];
-
- if (evalCode == EVAL_TOKENS && evalCode2 != 0) {
- evalCode = evalCode2;
- evalCode2 = 0;
- }
-
- voutPubkeys.push_back(pubkey2pk(ParseHex(CC_BURNPUBKEY)));
-
- int64_t burnedAmount = 0;
-
- for (int i = 0; i < tx.vout.size(); i++) {
-
- if (tx.vout[i].scriptPubKey.IsPayToCryptoCondition())
- {
- // make all possible token vouts for dead pk:
- for (std::vector::iterator it = voutPubkeys.begin(); it != voutPubkeys.end(); it++) {
- testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[i].nValue, *it), std::string("single-eval cc1 burn pk")));
- testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode, evalCode2, tx.vout[i].nValue, *it), std::string("three-eval cc1 burn pk")));
-
- if (evalCode2 != 0)
- // also check in backward evalcode order:
- testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, evalCode, tx.vout[i].nValue, *it), std::string("three-eval cc1 burn pk backward-eval")));
- }
-
- // try all test vouts:
- for (auto t : testVouts) {
- if (t.first == tx.vout[i]) {
- LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << "HasBurnedTokensvouts() burned amount=" << tx.vout[i].nValue << " msg=" << t.second << " evalCode=" << (int)evalCode << " evalCode2=" << (int)evalCode2 << " txid=" << tx.GetHash().GetHex() << " tokenid=" << reftokenid.GetHex() << std::endl);
- burnedAmount += tx.vout[i].nValue;
- }
- }
- LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << "HasBurnedTokensvouts() total burned=" << burnedAmount << " evalCode=" << (int)evalCode << " evalCode2=" << (int)evalCode2 << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl);
- }
- }
-
- return burnedAmount;
-}
-
-CPubKey GetTokenOriginatorPubKey(CScript scriptPubKey) {
-
- uint8_t funcId, evalCode;
- uint256 tokenid;
- std::vector voutTokenPubkeys;
- std::vector> oprets;
-
- if ((funcId = DecodeTokenOpRet(scriptPubKey, evalCode, tokenid, voutTokenPubkeys, oprets)) != 0) {
- CTransaction tokenbasetx;
- uint256 hashBlock;
-
- if (myGetTransaction(tokenid, tokenbasetx, hashBlock) && tokenbasetx.vout.size() > 0) {
- vscript_t vorigpubkey;
- std::string name, desc;
- if (DecodeTokenCreateOpRet(tokenbasetx.vout.back().scriptPubKey, vorigpubkey, name, desc) != 0)
- return pubkey2pk(vorigpubkey);
- }
- }
- return CPubKey(); //return invalid pubkey
-}
-
-// returns token creation signed raw tx
-std::string CreateToken(int64_t txfee, int64_t tokensupply, std::string name, std::string description, vscript_t nonfungibleData)
-{
- CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
- CPubKey mypk; struct CCcontract_info *cp, C;
- if (tokensupply < 0) {
- CCerror = "negative tokensupply";
- LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "CreateToken() =" << CCerror << "=" << tokensupply << std::endl);
- return std::string("");
- }
- if (!nonfungibleData.empty() && tokensupply != 1) {
- CCerror = "for non-fungible tokens tokensupply should be equal to 1";
- LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "CreateToken() " << CCerror << std::endl);
- return std::string("");
- }
-
-
- cp = CCinit(&C, EVAL_TOKENS);
- if (name.size() > 32 || description.size() > 4096) // this is also checked on rpc level
- {
- LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << "name len=" << name.size() << " or description len=" << description.size() << " is too big" << std::endl);
- CCerror = "name should be <= 32, description should be <= 4096";
- return("");
- }
- if (txfee == 0)
- txfee = 10000;
- mypk = pubkey2pk(Mypubkey());
-
- if (AddNormalinputs2(mtx, tokensupply + 2 * txfee, 64) > 0) // add normal inputs only from mypk
- {
- int64_t mypkInputs = TotalPubkeyNormalInputs(mtx, mypk);
- if (mypkInputs < tokensupply) { // check that tokens amount are really issued with mypk (because in the wallet there maybe other privkeys)
- CCerror = "some inputs signed not with -pubkey=pk";
- return std::string("");
- }
-
- uint8_t destEvalCode = EVAL_TOKENS;
- if( nonfungibleData.size() > 0 )
- destEvalCode = nonfungibleData.begin()[0];
-
- // NOTE: we should prevent spending fake-tokens from this marker in IsTokenvout():
- mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, txfee, GetUnspendable(cp, NULL))); // new marker to token cc addr, burnable and validated, vout pos now changed to 0 (from 1)
- mtx.vout.push_back(MakeTokensCC1vout(destEvalCode, tokensupply, mypk));
- //mtx.vout.push_back(CTxOut(txfee, CScript() << ParseHex(cp->CChexstr) << OP_CHECKSIG)); // old marker (non-burnable because spending could not be validated)
- //mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, txfee, GetUnspendable(cp, NULL))); // ...moved to vout=0 for matching with rogue-game token
-
- return(FinalizeCCTx(0, cp, mtx, mypk, txfee, EncodeTokenCreateOpRet('c', Mypubkey(), name, description, nonfungibleData)));
- }
-
- CCerror = "cant find normal inputs";
- LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "CreateToken() " << CCerror << std::endl);
- return std::string("");
-}
-
-// transfer tokens to another pubkey
-// param additionalEvalCode allows transfer of dual-eval non-fungible tokens
-std::string TokenTransfer(int64_t txfee, uint256 tokenid, vscript_t destpubkey, int64_t total)
-{
- CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
- CPubKey mypk; uint64_t mask; int64_t CCchange = 0, inputs = 0; struct CCcontract_info *cp, C;
- vscript_t vopretNonfungible, vopretEmpty;
-
- if (total < 0) {
- CCerror = strprintf("negative total");
- LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << CCerror << "=" << total << std::endl);
- return("");
- }
-
- cp = CCinit(&C, EVAL_TOKENS);
-
- if (txfee == 0)
- txfee = 10000;
- mypk = pubkey2pk(Mypubkey());
- /*if ( cp->tokens1of2addr[0] == 0 )
- {
- GetTokensCCaddress(cp, cp->tokens1of2addr, mypk);
- fprintf(stderr,"set tokens1of2addr <- %s\n",cp->tokens1of2addr);
- }*/
- if (AddNormalinputs(mtx, mypk, txfee, 3) > 0)
- {
- mask = ~((1LL << mtx.vin.size()) - 1); // seems, mask is not used anymore
-
- if ((inputs = AddTokenCCInputs(cp, mtx, mypk, tokenid, total, 60, vopretNonfungible)) > 0) // NOTE: AddTokenCCInputs might set cp->additionalEvalCode which is used in FinalizeCCtx!
- {
- if (inputs < total) { //added dimxy
- CCerror = strprintf("insufficient token inputs");
- LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "TokenTransfer() " << CCerror << std::endl);
- return std::string("");
- }
-
- uint8_t destEvalCode = EVAL_TOKENS;
- if (vopretNonfungible.size() > 0)
- destEvalCode = vopretNonfungible.begin()[0];
-
- if (inputs > total)
- CCchange = (inputs - total);
- mtx.vout.push_back(MakeTokensCC1vout(destEvalCode, total, pubkey2pk(destpubkey))); // if destEvalCode == EVAL_TOKENS then it is actually MakeCC1vout(EVAL_TOKENS,...)
- if (CCchange != 0)
- mtx.vout.push_back(MakeTokensCC1vout(destEvalCode, CCchange, mypk));
-
- std::vector voutTokenPubkeys;
- voutTokenPubkeys.push_back(pubkey2pk(destpubkey)); // dest pubkey for validating vout
-
- return FinalizeCCTx(mask, cp, mtx, mypk, txfee, EncodeTokenOpRet(tokenid, voutTokenPubkeys, std::make_pair((uint8_t)0, vopretEmpty)));
- }
- else {
- CCerror = strprintf("no token inputs");
- LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "TokenTransfer() " << CCerror << " for amount=" << total << std::endl);
- }
- //} else fprintf(stderr,"numoutputs.%d != numamounts.%d\n",n,(int32_t)amounts.size());
- }
- else {
- CCerror = strprintf("insufficient normal inputs for tx fee");
- LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "TokenTransfer() " << CCerror << std::endl);
- }
- return("");
-}
-
-
-int64_t GetTokenBalance(CPubKey pk, uint256 tokenid)
-{
- uint256 hashBlock;
- CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
- CTransaction tokentx;
-
- // CCerror = strprintf("obsolete, cannot return correct value without eval");
- // return 0;
-
- if (myGetTransaction(tokenid, tokentx, hashBlock) == 0)
- {
- LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "cant find tokenid" << std::endl);
- CCerror = strprintf("cant find tokenid");
- return 0;
- }
-
- struct CCcontract_info *cp, C;
- cp = CCinit(&C, EVAL_TOKENS);
- return(AddTokenCCInputs(cp, mtx, pk, tokenid, 0, 0));
-}
-
-UniValue TokenInfo(uint256 tokenid)
-{
- UniValue result(UniValue::VOBJ);
- uint256 hashBlock;
- CTransaction tokenbaseTx;
- std::vector origpubkey;
- std::vector> oprets;
- vscript_t vopretNonfungible;
- std::string name, description;
- struct CCcontract_info *cpTokens, tokensCCinfo;
-
- cpTokens = CCinit(&tokensCCinfo, EVAL_TOKENS);
-
- if( !myGetTransaction(tokenid, tokenbaseTx, hashBlock) )
- {
- fprintf(stderr, "TokenInfo() cant find tokenid\n");
- result.push_back(Pair("result", "error"));
- result.push_back(Pair("error", "cant find tokenid"));
- return(result);
- }
- if ( HUSH_NSPV_FULLNODE && hashBlock.IsNull()) {
- result.push_back(Pair("result", "error"));
- result.push_back(Pair("error", "the transaction is still in mempool"));
- return(result);
- }
-
- if (tokenbaseTx.vout.size() > 0 && DecodeTokenCreateOpRet(tokenbaseTx.vout[tokenbaseTx.vout.size() - 1].scriptPubKey, origpubkey, name, description, oprets) != 'c')
- {
- LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "TokenInfo() passed tokenid isnt token creation txid" << std::endl);
- result.push_back(Pair("result", "error"));
- result.push_back(Pair("error", "tokenid isnt token creation txid"));
- return result;
- }
- result.push_back(Pair("result", "success"));
- result.push_back(Pair("tokenid", tokenid.GetHex()));
- result.push_back(Pair("owner", HexStr(origpubkey)));
- result.push_back(Pair("name", name));
-
- int64_t supply = 0, output;
- for (int v = 0; v < tokenbaseTx.vout.size() - 1; v++)
- if ((output = IsTokensvout(false, true, cpTokens, NULL, tokenbaseTx, v, tokenid)) > 0)
- supply += output;
- result.push_back(Pair("supply", supply));
- result.push_back(Pair("description", description));
-
- GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vopretNonfungible);
- if( !vopretNonfungible.empty() )
- result.push_back(Pair("data", HexStr(vopretNonfungible)));
-
- if (tokenbaseTx.IsCoinImport()) { // if imported token
- ImportProof proof;
- CTransaction burnTx;
- std::vector payouts;
- CTxDestination importaddress;
-
- std::string sourceSymbol = "can't decode";
- std::string sourceTokenId = "can't decode";
-
- if (UnmarshalImportTx(tokenbaseTx, proof, burnTx, payouts))
- {
- // extract op_return to get burn source chain.
- std::vector burnOpret;
- std::string targetSymbol;
- uint32_t targetCCid;
- uint256 payoutsHash;
- std::vector rawproof;
- if (UnmarshalBurnTx(burnTx, targetSymbol, &targetCCid, payoutsHash, rawproof)) {
- if (rawproof.size() > 0) {
- CTransaction tokenbasetx;
- E_UNMARSHAL(rawproof, ss >> sourceSymbol;
- if (!ss.eof())
- ss >> tokenbasetx);
-
- if (!tokenbasetx.IsNull())
- sourceTokenId = tokenbasetx.GetHash().GetHex();
- }
- }
- }
- result.push_back(Pair("IsImported", "yes"));
- result.push_back(Pair("sourceChain", sourceSymbol));
- result.push_back(Pair("sourceTokenId", sourceTokenId));
- }
-
- return result;
-}
-
-UniValue TokenList()
-{
- UniValue result(UniValue::VARR);
- std::vector txids;
- std::vector > addressIndexCCMarker;
-
- struct CCcontract_info *cp, C; uint256 txid, hashBlock;
- CTransaction vintx; std::vector origpubkey;
- std::string name, description;
-
- cp = CCinit(&C, EVAL_TOKENS);
-
- auto addTokenId = [&](uint256 txid) {
- if (myGetTransaction(txid, vintx, hashBlock) != 0) {
- if (vintx.vout.size() > 0 && DecodeTokenCreateOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, origpubkey, name, description) != 0) {
- result.push_back(txid.GetHex());
- }
- }
- };
-
- SetCCtxids(txids, cp->normaladdr,false,cp->evalcode,zeroid,'c'); // find by old normal addr marker
- for (std::vector::const_iterator it = txids.begin(); it != txids.end(); it++) {
- addTokenId(*it);
- }
-
- SetCCunspents(addressIndexCCMarker, cp->unspendableCCaddr,true); // find by burnable validated cc addr marker
- for (std::vector >::const_iterator it = addressIndexCCMarker.begin(); it != addressIndexCCMarker.end(); it++) {
- addTokenId(it->first.txhash);
- }
-
- return(result);
-}
diff --git a/src/cc/Makefile_rogue b/src/cc/Makefile_rogue
deleted file mode 100644
index 3b2f65e00..000000000
--- a/src/cc/Makefile_rogue
+++ /dev/null
@@ -1,38 +0,0 @@
-SHELL = /bin/sh
-CC = gcc
-CC_DARWIN = g++-8
-CC_WIN = x86_64-w64-mingw32-gcc-posix
-CFLAGS_DARWIN = -DBUILD_ROGUE -std=c++11 -arch x86_64 -I../../depends/$(shell echo `../..//depends/config.guess`/include) -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -Wl,-undefined -Wl,dynamic_lookup -Wno-write-strings -shared -dynamiclib
-CFLAGS = -Wno-write-strings -DBUILD_ROGUE -std=c++11 -I../../depends/$(shell echo `../..//depends/config.guess`/include) -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared
-CFLAGS_WIN = -Wno-write-strings -DBUILD_ROGUE -std=c++11 -I./rogue/x86_64-w64-mingw32/include -I./rogue/x86_64-w64-mingw32/include/ncursesw -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared
-DEBUGFLAGS = -O0 -D _DEBUG
-RELEASEFLAGS = -O2 -D NDEBUG -combine -fwhole-program
-$(info $(OS))
-OS := $(shell uname -s)
-$(info $(OS))
-TARGET = librogue.so
-TARGET_DARWIN = librogue.dylib
-TARGET_WIN = librogue.dll
-SOURCES = cclib.cpp
-#HEADERS = $(shell echo ../cryptoconditions/include/*.h) -I/usr/local/Cellar/gcc\@8/8.3.0/include/c++/8.3.0/
-
-all: $(TARGET)
-
-$(TARGET): $(SOURCES)
- $(info Building cclib to src/)
-ifeq ($(OS),Darwin)
- $(CC_DARWIN) $(CFLAGS_DARWIN) $(DEBUGFLAGS) -o $(TARGET_DARWIN) -c $(SOURCES)
- cp $(TARGET_DARWIN) ../libcc.dylib
-else ifeq ($(HOST),x86_64-w64-mingw32)
- $(info WINDOWS)
- $(CC_WIN) $(CFLAGS_WIN) $(DEBUGFLAGS) -o $(TARGET_WIN) -c $(SOURCES)
- cp $(TARGET_WIN) ../libcc.dll
-#else ifeq ($(WIN_HOST),True) - todo: pass ENV var from build.sh if WIN host
-else
- $(info LINUX)
- $(CC) $(CFLAGS) $(DEBUGFLAGS) -o $(TARGET) -c $(SOURCES)
- cp $(TARGET) ../libcc.so
-endif
-
-clean:
- rm -rf $(TARGET)
diff --git a/src/cc/assets.cpp b/src/cc/assets.cpp
index d2ec4f21b..32e3482d3 100644
--- a/src/cc/assets.cpp
+++ b/src/cc/assets.cpp
@@ -19,448 +19,8 @@
#include "CCassets.h"
#include "CCtokens.h"
-/*
- Assets can be created or transferred.
-
- native coins are also locked in the EVAL_ASSETS address, so we need a strict rule on when utxo in the special address are native coins and when they are assets. The specific rule that must not be violated is that vout0 for 'b'/'B' funcid are native coins. All other utxo locked in the special address are assets.
-
- To create an asset use CC EVAL_ASSETS to create a transaction where vout[0] funds the assets. Externally each satoshi can be interpreted to represent 1 asset, or 100 million satoshis for one asset with 8 decimals, and the other decimals in between. The interpretation of the number of decimals is left to the higher level usages.
-
- Once created, the assetid is the txid of the create transaction and using the assetid/0 it can spend the assets to however many outputs it creates. The restriction is that the last output must be an opreturn with the assetid. The sum of all but the first output needs to add up to the total assetoshis input. The first output is ignored and used for change from txfee.
-
- What this means is that vout 0 of the creation txid and vouts 1 ... n-2 for transfer vouts are assetoshi outputs.
-
- There is a special type of transfer to an unspendable address, that locks the asset and creates an offer for all. The details specified in the opreturn. In order to be compatible with the signing restrictions, the "unspendable" address is actually an address whose privkey is known to all. Funds sent to this address can only be spent if the swap parameters are fulfilled, or if the original pubkey cancels the offer by spending it.
-
- Types of transactions:
- create name:description -> txid for assetid
- transfer -> [{address:amount}, ... ] // like withdraw api
- selloffer -> cancel or fillsell -> mempool txid or rejected, might not confirm
- buyoffer -> cancelbuy or fillbuy -> mempool txid or rejected, might not confirm
- exchange -> cancel or fillexchange -> mempool txid or rejected, might not confirm
-
- assetsaddress // all assets end up in a special address for each pubkey
- assetbalance
- assetutxos
- assetsbalances
- asks
- bids
- swaps
-
- valid CC output: create or transfer or buyoffer or selloffer or exchange or cancel or fill
-
-
- buyoffer:
- vins.*: normal inputs (bid + change)
- vout.0: amount of bid to unspendable
- vout.1: CC output for marker
- vout.2: 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]
- vin.2: CC marker from buyoffer for txfee
- vout.0: vin.1 value to original pubkey buyTx.vout[0].nValue -> [origpubkey]
- vout.1: vin.2 back to users pubkey
- vout.2: normal output for change (if any)
- vout.n-1: opreturn [EVAL_ASSETS] ['o'] [assetid] 0 0 [origpubkey]
-
- 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 marker
- vout.2: CC output for change (if any)
- vout.3: normal output for change (if any)
- vout.n-1: opreturn [EVAL_ASSETS] ['s'] [assetid] [amount of native coin required] [origpubkey]
-
- exchange:
- vin.0: normal input
- vin.1+: valid CC output
- 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] ['e'] [assetid] [assetid2] [amount of asset2 required] [origpubkey]
-
- cancel:
- vin.0: normal input
- vin.1: unspendable.(vout.0 from exchange or selloffer) sellTx/exchangeTx.vout[0] inputTx
- vin.2: CC marker from selloffer for txfee
- vout.0: vin.1 assetoshis to original pubkey CC sellTx/exchangeTx.vout[0].nValue -> [origpubkey]
- vout.1: vin.2 back to users pubkey
- vout.2: 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]
-
- fillexchange:
- vin.0: normal input
- vin.1: unspendable.(vout.0 assetoshis from exchange) exchangeTx.vout[0]
- vin.2+: valid CC assetid2 output that satisfies exchange (*tx.vin[2])->nValue
- vout.0: remaining assetoshis -> unspendable
- vout.1: vin.1 assetoshis to signer of vin.2 exchangeTx.vout[0].nValue -> any
- vout.2: vin.2 assetoshis2 to original pubkey [origpubkey]
- vout.3: normal output for change (if any)
- vout.n-1: opreturn [EVAL_ASSETS] ['E'] [assetid vin0+1] [assetid vin2] [remaining asset2 required] [origpubkey]
-*/
-
-
-
-
// tx validation
bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransaction &tx, uint32_t nIn)
{
- static uint256 zero;
- CTxDestination address;
- CTransaction vinTx, createTx;
- uint256 hashBlock, assetid, assetid2;
- int32_t i,starti, numvins, numvouts, preventCCvins, preventCCvouts;
- int64_t remaining_price, nValue, assetoshis, outputsDummy,inputs,tmpprice,totalunits,ignore;
- std::vector origpubkey, tmporigpubkey, ignorepubkey, vopretNonfungible, vopretNonfungibleDummy;
- uint8_t funcid, evalCodeInOpret;
- char destaddr[64], origNormalAddr[64], origTokensCCaddr[64], origCCaddrDummy[64];
- char tokensDualEvalUnspendableCCaddr[64], origAssetsCCaddr[64];
-
- //return true;
-
- numvins = tx.vin.size();
- numvouts = tx.vout.size();
- outputsDummy = inputs = 0;
- preventCCvins = preventCCvouts = -1;
-
- if (numvouts == 0)
- return eval->Invalid("AssetValidate: no vouts");
-
- if((funcid = DecodeAssetTokenOpRet(tx.vout[numvouts-1].scriptPubKey, evalCodeInOpret, assetid, assetid2, remaining_price, origpubkey)) == 0 )
- return eval->Invalid("AssetValidate: invalid opreturn payload");
-
- // non-fungible tokens support:
- GetNonfungibleData(assetid, vopretNonfungible);
- if (vopretNonfungible.size() > 0)
- cpAssets->additionalTokensEvalcode2 = vopretNonfungible.begin()[0];
-
- // find dual-eval tokens unspendable addr:
- GetTokensCCaddress(cpAssets, tokensDualEvalUnspendableCCaddr, GetUnspendable(cpAssets, NULL));
- // this is for marker validation:
- GetCCaddress(cpAssets, origAssetsCCaddr, origpubkey);
-
- // we need this for validating single-eval tokens' vins/vous:
- struct CCcontract_info *cpTokens, tokensC;
- cpTokens = CCinit(&tokensC, EVAL_TOKENS);
-
- // find single-eval token user cc addr:
- //GetCCaddress(cpTokens, signleEvalTokensCCaddr, pubkey2pk(origpubkey));
-
- //fprintf(stderr,"AssetValidate (%c)\n",funcid);
-
- if( funcid != 'o' && funcid != 'x' && eval->GetTxUnconfirmed(assetid, createTx, hashBlock) == 0 )
- return eval->Invalid("cant find asset create txid");
- else if( funcid != 'o' && funcid != 'x' && assetid2 != zero && eval->GetTxUnconfirmed(assetid2, createTx, hashBlock) == 0 )
- return eval->Invalid("cant find asset2 create txid");
- else if( IsCCInput(tx.vin[0].scriptSig) != 0 ) // vin0 should be normal vin
- return eval->Invalid("illegal asset vin0");
- else if( numvouts < 2 )
- return eval->Invalid("too few vouts"); // it was if(numvouts < 1) but it refers at least to vout[1] below
- else if( funcid != 'c' )
- {
- /* if( funcid == 't' )
- starti = 0;
- else
- starti = 1; */
-
- if( assetid == zero )
- return eval->Invalid("illegal assetid");
-
- else if (!AssetCalcAmounts(cpAssets, inputs, outputsDummy/*outputsDummy is calculated incorrectly but not used*/, eval, tx, assetid)) { // Only set inputs and outputs. NOTE: we do not need to check cc inputs == cc outputs
- return false; // returns false if some problems with reading vintxes
- }
- }
-
- switch( funcid )
- {
- case 'c': // create wont be called to be verified as it has no CC inputs
- //vin.0: normal input
- //vout.0: issuance assetoshis to CC
- //vout.1: normal output for change (if any)
- //vout.n-1: opreturn [EVAL_ASSETS] ['c'] [{"":""}]
- //if (evalCodeInOpret == EVAL_ASSETS)
- // return eval->Invalid("unexpected AssetValidate for createasset");
- // return
- return eval->Invalid("invalid asset funcid \'c\'");
- break;
- case 't': // 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]
- //if (inputs == 0)
- // return eval->Invalid("no asset inputs for transfer");
- //fprintf(stderr,"transfer preliminarily validated %.8f -> %.8f (%d %d)\n",(double)inputs/COIN,(double)outputs/COIN,preventCCvins,preventCCvouts);
- return eval->Invalid("invalid asset funcid \'t\'");
- break;
-
- case 'b': // buyoffer
- //vins.*: normal inputs (bid + change)
- //vout.0: amount of bid to unspendable
- //vout.1: CC output for marker
- //vout.2: normal output for change (if any)
- // vout.n-1: opreturn [EVAL_ASSETS] ['b'] [assetid] [amount of asset required] [origpubkey]
-
- // as we don't use tokenconvert we should not be here:
- return eval->Invalid("invalid asset funcid (b)");
-
- if( remaining_price == 0 )
- return eval->Invalid("illegal null amount for buyoffer");
- else if( ConstrainVout(tx.vout[0], 1, cpAssets->unspendableCCaddr,0) == 0 ) // coins to assets unspendable cc addr
- return eval->Invalid("invalid vout for buyoffer");
- preventCCvins = 1;
- preventCCvouts = 1;
- fprintf(stderr,"buy offer validated to destaddr.(%s)\n",cpAssets->unspendableCCaddr);
- break;
-
- case 'o': // cancelbuy
- //vin.0: normal input
- //vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0]
- //vin.2: CC marker from buyoffer for txfee
- //vout.0: vin.1 value to original pubkey buyTx.vout[0].nValue -> [origpubkey]
- //vout.1: vin.2 back to users pubkey
- //vout.2: normal output for change (if any)
- //vout.n-1: opreturn [EVAL_ASSETS] ['o']
- if( (nValue= AssetValidateBuyvin(cpAssets, eval, tmpprice, tmporigpubkey, origCCaddrDummy, origNormalAddr, tx, assetid)) == 0 )
- return(false);
- else if( ConstrainVout(tx.vout[0],0, origNormalAddr, nValue) == 0 )
- return eval->Invalid("invalid refund for cancelbuy");
- preventCCvins = 3;
- preventCCvouts = 0;
- //fprintf(stderr,"cancelbuy validated to origaddr.(%s)\n",origNormalAddr);
- break;
-
- case 'B': // 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]
- preventCCvouts = 4;
-
- if( (nValue = AssetValidateBuyvin(cpAssets, eval, totalunits, tmporigpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 )
- return(false);
- else if( numvouts < 4 )
- return eval->Invalid("not enough vouts for fillbuy");
- else if( tmporigpubkey != origpubkey )
- return eval->Invalid("mismatched origpubkeys for fillbuy");
- else
- {
- if( nValue != tx.vout[0].nValue + tx.vout[1].nValue )
- return eval->Invalid("locked value doesnt match vout0+1 fillbuy");
- else if( tx.vout[4].scriptPubKey.IsPayToCryptoCondition() != 0 ) // if cc change present
- {
- if( ConstrainVout(tx.vout[2], 1, origTokensCCaddr, 0) == 0 ) // tokens to originator cc addr (tokens+nonfungible evals)
- return eval->Invalid("vout2 doesnt go to origpubkey fillbuy");
- else if ( inputs != tx.vout[2].nValue + tx.vout[4].nValue )
- return eval->Invalid("asset inputs doesnt match vout2+3 fillbuy");
- preventCCvouts ++;
- }
- else if( ConstrainVout(tx.vout[2], 1, origTokensCCaddr, inputs) == 0 ) // tokens to originator cc addr (tokens+nonfungible evals)
- return eval->Invalid("vout2 doesnt match inputs fillbuy");
- else if( ConstrainVout(tx.vout[1], 0, NULL, 0) == 0 )
- return eval->Invalid("vout1 is CC for fillbuy");
- else if( ConstrainVout(tx.vout[3], 1, origAssetsCCaddr, 10000) == 0 ) // marker to asset cc addr
- return eval->Invalid("invalid marker for original pubkey");
- else if( ValidateBidRemainder(remaining_price, tx.vout[0].nValue, nValue, tx.vout[1].nValue, tx.vout[2].nValue, totalunits) == false )
- return eval->Invalid("mismatched remainder for fillbuy");
- else if( remaining_price != 0 )
- {
- if( ConstrainVout(tx.vout[0], 1, cpAssets->unspendableCCaddr, 0) == 0 ) // coins to asset unspendable cc addr
- return eval->Invalid("mismatched vout0 AssetsCCaddr for fillbuy");
- }
- }
- //fprintf(stderr,"fillbuy validated\n");
- break;
- //case 'e': // selloffer
- // break; // disable swaps
- case 's': // 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 marker
- //vout.2: CC output for change (if any)
- //vout.3: normal output for change (if any)
- //'s'.vout.n-1: opreturn [EVAL_ASSETS] ['s'] [assetid] [amount of native coin required] [origpubkey]
- //'e'.vout.n-1: opreturn [EVAL_ASSETS] ['e'] [assetid] [assetid2] [amount of asset2 required] [origpubkey]
-
- // as we don't use tokenconvert we should not be here:
- return eval->Invalid("invalid asset funcid (s)");
-
- preventCCvouts = 2;
- if( remaining_price == 0 )
- return eval->Invalid("illegal null remaining_price for selloffer");
- if ( tx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 )
- return eval->Invalid("invalid normal vout1 for sellvin");
- if( tx.vout[2].scriptPubKey.IsPayToCryptoCondition() != 0 ) // if cc change presents
- {
- preventCCvouts++;
- if( ConstrainVout(tx.vout[0], 1, (char *)cpTokens->unspendableCCaddr, 0) == 0 ) // tokens to tokens unspendable cc addr. TODO: this in incorrect, should be assets if we got here!
- return eval->Invalid("mismatched vout0 TokensCCaddr for selloffer");
- else if( tx.vout[0].nValue + tx.vout[2].nValue != inputs )
- return eval->Invalid("mismatched vout0+vout2 total for selloffer");
- }
- // no cc change:
- else if( ConstrainVout(tx.vout[0], 1, (char *)cpTokens->unspendableCCaddr, inputs) == 0 ) // tokens to tokens unspendable cc addr TODO: this in incorrect, should be assets if got here!
- return eval->Invalid("mismatched vout0 TokensCCaddr for selloffer");
- //fprintf(stderr,"remaining.%d for sell\n",(int32_t)remaining_price);
- break;
-
- case 'x': // cancel sell
- //vin.0: normal input
- //vin.1: unspendable.(vout.0 from exchange or selloffer) sellTx/exchangeTx.vout[0] inputTx
- //vin.2: CC marker from selloffer for txfee
- //vout.0: vin.1 assetoshis to original pubkey CC sellTx/exchangeTx.vout[0].nValue -> [origpubkey]
- //vout.1: vin.2 back to users pubkey
- //vout.2: normal output for change (if any)
- //vout.n-1: opreturn [EVAL_ASSETS] ['x'] [assetid]
-
- if( (assetoshis = AssetValidateSellvin(cpAssets, eval, tmpprice, tmporigpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 ) // NOTE:
- return(false);
- else if( ConstrainVout(tx.vout[0], 1, origTokensCCaddr, assetoshis) == 0 ) // tokens returning to originator cc addr
- return eval->Invalid("invalid vout for cancel");
- preventCCvins = 3;
- preventCCvouts = 1;
- break;
-
- case 'S': // fillsell
- //vin.0: normal input
- //vin.1: unspendable.(vout.0 assetoshis from selloffer) sellTx.vout[0]
- //'S'.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
- //'S'.vout.2: vin.2 value to original pubkey [origpubkey]
- //vout.3: normal output for change (if any)
- //'S'.vout.n-1: opreturn [EVAL_ASSETS] ['S'] [assetid] [amount of coin still required] [origpubkey]
-
- if( (assetoshis = AssetValidateSellvin(cpAssets, eval, totalunits, tmporigpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 )
- return(false);
- else if( numvouts < 4 )
- return eval->Invalid("not enough vouts for fillask");
- else if( tmporigpubkey != origpubkey )
- return eval->Invalid("mismatched origpubkeys for fillask");
- else
- {
- if( assetoshis != tx.vout[0].nValue + tx.vout[1].nValue )
- return eval->Invalid("locked value doesnt match vout0+1 fillask");
- if( ValidateAskRemainder(remaining_price, tx.vout[0].nValue, assetoshis, tx.vout[1].nValue, tx.vout[2].nValue, totalunits) == false )
- return eval->Invalid("mismatched remainder for fillask");
- else if( ConstrainVout(tx.vout[1], 1, NULL, 0) == 0 ) // do not check token buyer's cc addr
- return eval->Invalid("normal vout1 for fillask");
- else if( ConstrainVout(tx.vout[2], 0, origNormalAddr, 0) == 0 ) // coins to originator normal addr
- return eval->Invalid("normal vout1 for fillask");
- else if( ConstrainVout(tx.vout[3], 1, origAssetsCCaddr, 10000) == 0 ) // marker to originator asset cc addr
- return eval->Invalid("invalid marker for original pubkey");
- else if( remaining_price != 0 )
- {
- if ( ConstrainVout(tx.vout[0], 1, tokensDualEvalUnspendableCCaddr, 0) == 0 )
- return eval->Invalid("mismatched vout0 assets dual unspendable CCaddr for fill sell");
- }
- }
- //fprintf(stderr,"fill validated\n");
- break;
- case 'E': // fillexchange
- ////////// not implemented yet ////////////
- return eval->Invalid("unexpected assets fillexchange funcid");
- break; // disable asset swaps
- //vin.0: normal input
- //vin.1: unspendable.(vout.0 assetoshis from selloffer) sellTx.vout[0]
- //vin.2+: valid CC assetid2 output that satisfies exchange (*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+ assetoshis2 to original pubkey [origpubkey]
- //vout.3: CC output for asset2 change (if any)
- //vout.3/4: normal output for change (if any)
- //vout.n-1: opreturn [EVAL_ASSETS] ['E'] [assetid vin0+1] [assetid vin2] [remaining asset2 required] [origpubkey]
-
- //if ( AssetExactAmounts(false, cp,inputs,outputs,eval,tx,assetid2) == false )
- // eval->Invalid("asset2 inputs != outputs");
-
- ////////// not implemented yet ////////////
- if( (assetoshis= AssetValidateSellvin(cpTokens, eval, totalunits, tmporigpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 )
- return(false);
- else if( numvouts < 3 )
- return eval->Invalid("not enough vouts for fillex");
- else if( tmporigpubkey != origpubkey )
- return eval->Invalid("mismatched origpubkeys for fillex");
- else
- {
- if( assetoshis != tx.vout[0].nValue + tx.vout[1].nValue )
- return eval->Invalid("locked value doesnt match vout0+1 fillex");
- else if( tx.vout[3].scriptPubKey.IsPayToCryptoCondition() != 0 )
- ////////// not implemented yet ////////////
- {
- if( ConstrainVout(tx.vout[2], 1, origTokensCCaddr, 0) == 0 )
- return eval->Invalid("vout2 doesnt go to origpubkey fillex");
- else if( inputs != tx.vout[2].nValue + tx.vout[3].nValue )
- {
- fprintf(stderr,"inputs %.8f != %.8f + %.8f\n",(double)inputs/COIN,(double)tx.vout[2].nValue/COIN,(double)tx.vout[3].nValue/COIN);
- return eval->Invalid("asset inputs doesnt match vout2+3 fillex");
- }
- }
- ////////// not implemented yet ////////////
- else if( ConstrainVout(tx.vout[2], 1, origTokensCCaddr, inputs) == 0 )
- return eval->Invalid("vout2 doesnt match inputs fillex");
- else if( ConstrainVout(tx.vout[1], 0, 0, 0) == 0 )
- return eval->Invalid("vout1 is CC for fillex");
- fprintf(stderr,"assets vout0 %llu, vin1 %llu, vout2 %llu -> orig, vout1 %llu, total %llu\n",(long long)tx.vout[0].nValue,(long long)assetoshis,(long long)tx.vout[2].nValue,(long long)tx.vout[1].nValue,(long long)totalunits);
- if( ValidateSwapRemainder(remaining_price, tx.vout[0].nValue, assetoshis,tx.vout[1].nValue, tx.vout[2].nValue, totalunits) == false )
- return eval->Invalid("mismatched remainder for fillex");
- else if( ConstrainVout(tx.vout[1], 1, 0, 0) == 0 )
- ////////// not implemented yet ////////////
- return eval->Invalid("normal vout1 for fillex");
- else if( remaining_price != 0 )
- {
- if( ConstrainVout(tx.vout[0], 1, (char *)cpAssets->unspendableCCaddr, 0) == 0 ) // TODO: unsure about this, but this is not impl yet anyway
- return eval->Invalid("mismatched vout0 AssetsCCaddr for fillex");
- }
- }
- ////////// not implemented yet ////////////
- //fprintf(stderr,"fill validated\n");
- break;
-
- default:
- fprintf(stderr,"illegal assets funcid.(%c)\n",funcid);
- return eval->Invalid("unexpected assets funcid");
- //break;
- }
-
- // what does this do?
- bool bPrevent = PreventCC(eval, tx, preventCCvins, numvins, preventCCvouts, numvouts); // seems we do not need this call as we already checked vouts well
- //std::cerr << "AssetsValidate() PreventCC returned=" << bPrevent << std::endl;
- return (bPrevent);
+ return false;
}
-
-
diff --git a/src/cc/cclib.cpp b/src/cc/cclib.cpp
index 9f902a140..e35ec8c9d 100644
--- a/src/cc/cclib.cpp
+++ b/src/cc/cclib.cpp
@@ -26,7 +26,6 @@
#include "main.h"
#include "chain.h"
#include "core_io.h"
-#include "crosschain.h"
#define FAUCET2SIZE COIN
#define EVAL_FAUCET2 EVAL_FIRSTUSER
#ifdef BUILD_ROGUE
@@ -83,10 +82,6 @@ CClib_methods[] =
#elif BUILD_GAMESCC
RPC_FUNCS
#else
- { (char *)"sudoku", (char *)"gen", (char *)"", 0, 0, 'G', EVAL_SUDOKU },
- { (char *)"sudoku", (char *)"txidinfo", (char *)"txid", 1, 1, 'T', EVAL_SUDOKU },
- { (char *)"sudoku", (char *)"pending", (char *)"", 0, 0, 'U', EVAL_SUDOKU },
- { (char *)"sudoku", (char *)"solution", (char *)"txid solution timestamps[81]", 83, 83, 'S', EVAL_SUDOKU },
{ (char *)"musig", (char *)"calcmsg", (char *)"sendtxid scriptPubKey", 2, 2, 'C', EVAL_MUSIG },
{ (char *)"musig", (char *)"combine", (char *)"pubkeys ...", 2, 999999999, 'P', EVAL_MUSIG },
{ (char *)"musig", (char *)"session", (char *)"myindex,numsigners,combined_pk,pkhash,msg32", 5, 5, 'R', EVAL_MUSIG },
@@ -646,52 +641,11 @@ int32_t cclib_parsehash(uint8_t *hash32,cJSON *item,int32_t len)
} else return(-1);
}
-#ifdef BUILD_ROGUE
-#include "rogue_rpc.cpp"
-#include "rogue/cursesd.c"
-#include "rogue/vers.c"
-#include "rogue/extern.c"
-#include "rogue/armor.c"
-#include "rogue/chase.c"
-#include "rogue/command.c"
-#include "rogue/daemon.c"
-#include "rogue/daemons.c"
-#include "rogue/fight.c"
-#include "rogue/init.c"
-#include "rogue/io.c"
-#include "rogue/list.c"
-#include "rogue/mach_dep.c"
-#include "rogue/rogue.c"
-#include "rogue/xcrypt.c"
-#include "rogue/mdport.c"
-#include "rogue/misc.c"
-#include "rogue/monsters.c"
-#include "rogue/move.c"
-#include "rogue/new_level.c"
-#include "rogue/options.c"
-#include "rogue/pack.c"
-#include "rogue/passages.c"
-#include "rogue/potions.c"
-#include "rogue/rings.c"
-#include "rogue/rip.c"
-#include "rogue/rooms.c"
-#include "rogue/save.c"
-#include "rogue/scrolls.c"
-#include "rogue/state.c"
-#include "rogue/sticks.c"
-#include "rogue/things.c"
-#include "rogue/weapons.c"
-#include "rogue/wizard.c"
-#elif BUILD_CUSTOMCC
+#if BUILD_CUSTOMCC
#include "customcc.cpp"
-#elif BUILD_GAMESCC
-#include "rogue/cursesd.c"
-#include "gamescc.cpp"
-
#else
-#include "sudoku.cpp"
#include "musig.cpp"
#include "dilithium.c"
//#include "../secp256k1/src/modules/musig/example.c"
diff --git a/src/cc/channels.cpp b/src/cc/channels.cpp
deleted file mode 100644
index 498047196..000000000
--- a/src/cc/channels.cpp
+++ /dev/null
@@ -1,807 +0,0 @@
-// Copyright (c) 2016-2023 The Hush developers
-// Distributed under the GPLv3 software license, see the accompanying
-// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
-/******************************************************************************
- * Copyright © 2014-2019 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
-
-#define CC_MARKER_VALUE 10000
-
-int64_t IsChannelsvout(struct CCcontract_info *cp,const CTransaction& tx,CPubKey srcpub, CPubKey destpub,int32_t v)
-{
- char destaddr[65],channeladdr[65],tokenschanneladdr[65];
-
- GetCCaddress1of2(cp,channeladdr,srcpub,destpub);
- GetTokensCCaddress1of2(cp,tokenschanneladdr,srcpub,destpub);
- if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 )
- {
- if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && (strcmp(destaddr,channeladdr) == 0 || strcmp(destaddr,tokenschanneladdr) == 0))
- return(tx.vout[v].nValue);
- }
- return(0);
-}
-
-int64_t IsChannelsMarkervout(struct CCcontract_info *cp,const CTransaction& tx,CPubKey pubkey,int32_t v)
-{
- char destaddr[65],ccaddr[65];
-
- GetCCaddress(cp,ccaddr,pubkey);
- if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 )
- {
- if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,ccaddr) == 0 )
- return(tx.vout[v].nValue);
- }
- return(0);
-}
-
-CScript EncodeChannelsOpRet(uint8_t funcid,uint256 tokenid,uint256 opentxid,CPubKey srcpub,CPubKey destpub,int32_t numpayments,int64_t payment,uint256 hashchain)
-{
- CScript opret; uint8_t evalcode = EVAL_CHANNELS;
- vscript_t vopret;
-
- vopret = E_MARSHAL(ss << evalcode << funcid << opentxid << srcpub << destpub << numpayments << payment << hashchain);
- if (tokenid!=zeroid)
- {
- std::vector pks;
- pks.push_back(srcpub);
- pks.push_back(destpub);
- return(EncodeTokenOpRet(tokenid,pks, std::make_pair(OPRETID_CHANNELSDATA, vopret)));
- }
- opret << OP_RETURN << vopret;
- return(opret);
-}
-
-uint8_t DecodeChannelsOpRet(const CScript &scriptPubKey, uint256 &tokenid, uint256 &opentxid, CPubKey &srcpub,CPubKey &destpub,int32_t &numpayments,int64_t &payment,uint256 &hashchain)
-{
- std::vector> oprets;
- std::vector vopret,vOpretExtra; uint8_t *script,e,f,tokenevalcode;
- std::vector pubkeys;
-
- if (DecodeTokenOpRet(scriptPubKey,tokenevalcode,tokenid,pubkeys,oprets)!=0 && GetOpretBlob(oprets, OPRETID_CHANNELSDATA, vOpretExtra) && tokenevalcode==EVAL_TOKENS && vOpretExtra.size()>0)
- {
- vopret=vOpretExtra;
- }
- else 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 >> opentxid; ss >> srcpub; ss >> destpub; ss >> numpayments; ss >> payment; ss >> hashchain) != 0 )
- {
- return(f);
- }
- } else LOGSTREAM("channelscc",CCLOG_DEBUG1, stream << "script[0] " << script[0] << " != EVAL_CHANNELS" << std::endl);
- } else LOGSTREAM("channelscc",CCLOG_DEBUG1, stream << "not enough opret.[" << vopret.size() << "]" << std::endl);
- return(0);
-}
-
-bool ChannelsExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,int32_t minage,uint64_t txfee)
-{
- uint256 txid,param3,tokenid;
- CPubKey srcpub,destpub;
- int32_t param1,numvouts; int64_t param2; uint8_t funcid;
- CTransaction vinTx; uint256 hashBlock; int64_t inputs=0,outputs=0;
-
- if ((numvouts=tx.vout.size()) > 0 && (funcid=DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey, tokenid, txid, srcpub, destpub, param1, param2, param3))!=0)
- {
- switch (funcid)
- {
- case 'O':
- return (true);
- case 'P':
- if ( eval->GetTxUnconfirmed(tx.vin[1].prevout.hash,vinTx,hashBlock) == 0 )
- return eval->Invalid("cant find vinTx");
- inputs = vinTx.vout[tx.vin[1].prevout.n].nValue;
- outputs = tx.vout[0].nValue + tx.vout[3].nValue;
- break;
- case 'C':
- if ( eval->GetTxUnconfirmed(tx.vin[1].prevout.hash,vinTx,hashBlock) == 0 )
- return eval->Invalid("cant find vinTx");
- inputs = vinTx.vout[tx.vin[1].prevout.n].nValue;
- outputs = tx.vout[0].nValue;
- break;
- case 'R':
- if ( eval->GetTxUnconfirmed(tx.vin[1].prevout.hash,vinTx,hashBlock) == 0 )
- return eval->Invalid("cant find vinTx");
- inputs = vinTx.vout[tx.vin[1].prevout.n].nValue;
- outputs = tx.vout[2].nValue;
- break;
- default:
- return (false);
- }
- if ( inputs != outputs )
- {
- LOGSTREAM("channelscc",CCLOG_INFO, stream << "inputs " << inputs << " vs outputs " << outputs << std::endl);
- return eval->Invalid("mismatched inputs != outputs");
- }
- else return (true);
- }
- else
- {
- return eval->Invalid("invalid op_return data");
- }
- return(false);
-}
-
-bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn)
-{
- int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numpayments,p1,param1; bool retval;
- uint256 txid,hashblock,p3,param3,opentxid,tmp_txid,genhashchain,hashchain,tokenid;
- uint8_t funcid,hash[32],hashdest[32]; char channeladdress[65],srcmarker[65],destmarker[65],destaddr[65],srcaddr[65],desttokensaddr[65],srctokensaddr[65];
- int64_t p2,param2,payment;
- CPubKey srcpub, destpub;
- CTransaction channelOpenTx,channelCloseTx,prevTx;
-
- numvins = tx.vin.size();
- numvouts = tx.vout.size();
- preventCCvins = preventCCvouts = -1;
- if ( numvouts < 1 )
- return eval->Invalid("no vouts");
- else
- {
- if (ChannelsExactAmounts(cp,eval,tx,1,10000) == false )
- {
- return eval->Invalid("invalid channel inputs vs. outputs!");
- }
- else
- {
- txid = tx.GetHash();
- memcpy(hash,&txid,sizeof(hash));
- if ( (funcid = DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey, tokenid, opentxid, srcpub, destpub, param1, param2, param3)) != 0)
- {
- if (myGetTransaction(opentxid,channelOpenTx,hashblock)== 0)
- return eval->Invalid("invalid channelopen tx!");
- else if ((numvouts=channelOpenTx.vout.size()) > 0 && (DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, numpayments, payment, hashchain)) != 'O')
- return eval->Invalid("invalid channelopen OP_RETURN data!");
- GetCCaddress1of2(cp,channeladdress,srcpub,destpub);
- GetCCaddress(cp,srcmarker,srcpub);
- GetCCaddress(cp,destmarker,destpub);
- Getscriptaddress(srcaddr,CScript() << ParseHex(HexStr(srcpub)) << OP_CHECKSIG);
- Getscriptaddress(destaddr,CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG);
- _GetCCaddress(srctokensaddr,EVAL_TOKENS,srcpub);
- _GetCCaddress(desttokensaddr,EVAL_TOKENS,destpub);
- switch ( funcid )
- {
- case 'O':
- //vin.0: normal input
- //vout.0: CC vout for channel funding on CC1of2 pubkey
- //vout.1: CC vout marker to senders pubKey
- //vout.2: CC vout marker to receiver pubkey
- //vout.n-2: normal change
- //vout.n-1: opreturn - 'O' zerotxid senderspubkey receiverspubkey totalnumberofpayments paymentamount hashchain
- return eval->Invalid("unexpected ChannelsValidate for channelsopen!");
- case 'P':
- //vin.0: normal input
- //vin.1: CC input from channel funding
- //vin.2: CC input from src marker
- //vout.0: CC vout change to CC1of2 pubkey
- //vout.1: CC vout marker to senders pubKey
- //vout.2: CC vout marker to receiver pubkey
- //vout.3: normal output of payment amount to receiver pubkey
- //vout.n-2: normal change
- //vout.n-1: opreturn - 'P' opentxid senderspubkey receiverspubkey depth numpayments secret
- if ( IsCCInput(channelOpenTx.vin[0].scriptSig) != 0 )
- return eval->Invalid("vin.0 is normal for channelopen!");
- else if ( ConstrainVout(channelOpenTx.vout[0],1,channeladdress,numpayments*payment)==0 )
- return eval->Invalid("vout.0 is CC or invalid amount for channelopen!");
- else if ( ConstrainVout(channelOpenTx.vout[1],1,srcmarker,CC_MARKER_VALUE)==0 )
- return eval->Invalid("vout.1 is CC marker to srcpub or invalid amount for channelopen!");
- else if ( ConstrainVout(channelOpenTx.vout[2],1,destmarker,CC_MARKER_VALUE)==0 )
- return eval->Invalid("vout.2 is CC marker to destpub or invalid amount for channelopen!");
- else if (hush_txnotarizedconfirmed(opentxid) == 0)
- return eval->Invalid("channelopen is not yet confirmed(notarized)!");
- else if ( IsCCInput(tx.vin[0].scriptSig) != 0 )
- return eval->Invalid("vin.0 is normal for channelpayment!");
- else if ( IsCCInput(tx.vin[tx.vin.size()-2].scriptSig) == 0 )
- return eval->Invalid("vin."+std::to_string(tx.vin.size()-2)+" is CC for channelpayment!");
- else if ( ConstrainVout(tx.vout[1],1,srcmarker,CC_MARKER_VALUE)==0 )
- return eval->Invalid("vout.1 is CC marker to srcpub or invalid amount for channelpayment!");
- else if ( ConstrainVout(tx.vout[2],1,destmarker,CC_MARKER_VALUE)==0 )
- return eval->Invalid("vout.2 is CC marker to destpub or invalid amount for channelpayment!");
- else if ( tokenid!=zeroid && ConstrainVout(tx.vout[3],1,desttokensaddr,param2*payment)==0 )
- return eval->Invalid("vout.3 is CC or invalid amount or invalid receiver for channelpayment!");
- else if ( tokenid==zeroid && ConstrainVout(tx.vout[3],0,destaddr,param2*payment)==0 )
- return eval->Invalid("vout.3 is normal or invalid amount or invalid receiver for channelpayment!");
- else if ( param1 > CHANNELS_MAXPAYMENTS)
- return eval->Invalid("too many payment increments!");
- else
- {
- endiancpy(hash, (uint8_t * ) & param3, 32);
- for (i = 0; i < numpayments-param1; i++)
- {
- vcalc_sha256(0, hashdest, hash, 32);
- memcpy(hash, hashdest, 32);
- }
- endiancpy((uint8_t*)&genhashchain,hashdest,32);
- if (hashchain!=genhashchain)
- return eval->Invalid("invalid secret for payment, does not reach final hashchain!");
- else if (tx.vout[3].nValue != param2*payment)
- return eval->Invalid("vout amount does not match number_of_payments*payment!");
- if (myGetTransaction(tx.vin[1].prevout.hash,prevTx,hashblock) != 0)
- {
- if ((numvouts=prevTx.vout.size()) > 0 && DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, p1, p2, p3) == 0)
- return eval->Invalid("invalid previous tx OP_RETURN data!");
- else if ( ConstrainVout(tx.vout[0],1,channeladdress,(p1-param2)*payment)==0 )
- return eval->Invalid("vout.0 is CC or invalid CC change amount for channelpayment!");
- else if ((*cp->ismyvin)(tx.vin[tx.vin.size()-1].scriptSig) == 0 || prevTx.vout[tx.vin[tx.vin.size()-1].prevout.n].nValue!=CC_MARKER_VALUE)
- return eval->Invalid("vin."+std::to_string(tx.vin.size()-1)+" is CC marker or invalid marker amount for channelpayment!");
- else if (param1+param2!=p1)
- return eval->Invalid("invalid payment depth!");
- else if (tx.vout[3].nValue > prevTx.vout[0].nValue)
- return eval->Invalid("not enough funds in channel for that amount!");
- }
- }
- break;
- case 'C':
- //vin.0: normal input
- //vin.1: CC input from channel funding
- //vin.2: CC input from src marker
- //vout.0: CC vout for channel funding
- //vout.1: CC vout marker to senders pubKey
- //vout.2: CC vout marker to receiver pubkey
- //vout.n-2: normal change
- //vout.n-1: opreturn - 'C' opentxid senderspubkey receiverspubkey numpayments payment 0
- if ( IsCCInput(channelOpenTx.vin[0].scriptSig) != 0 )
- return eval->Invalid("vin.0 is normal for channelopen!");
- else if ( ConstrainVout(channelOpenTx.vout[0],1,channeladdress,numpayments*payment)==0 )
- return eval->Invalid("vout.0 is CC or invalid amount for channelopen!");
- else if ( ConstrainVout(channelOpenTx.vout[1],1,srcmarker,CC_MARKER_VALUE)==0 )
- return eval->Invalid("vout.1 is CC marker to srcpub or invalid amount for channelopen!");
- else if ( ConstrainVout(channelOpenTx.vout[2],1,destmarker,CC_MARKER_VALUE)==0 )
- return eval->Invalid("vout.2 is CC marker to destpub or invalid amount for channelopen!");
- else if (hush_txnotarizedconfirmed(opentxid) == 0)
- return eval->Invalid("channelopen is not yet confirmed(notarized)!");
- else if ( IsCCInput(tx.vin[0].scriptSig) != 0 )
- return eval->Invalid("vin.0 is normal for channelclose!");
- else if ( IsCCInput(tx.vin[tx.vin.size()-2].scriptSig) == 0 )
- return eval->Invalid("vin."+std::to_string(tx.vin.size()-2)+" is CC for channelclose!");
- else if ( ConstrainVout(tx.vout[0],1,channeladdress,0)==0 )
- return eval->Invalid("vout.0 is CC for channelclose!");
- else if ( ConstrainVout(tx.vout[1],1,srcmarker,CC_MARKER_VALUE)==0 )
- return eval->Invalid("vout.1 is CC marker to srcpub or invalid amount for channelclose!");
- else if ( ConstrainVout(tx.vout[2],1,destmarker,CC_MARKER_VALUE)==0 )
- return eval->Invalid("vout.2 is CC marker to destpub or invalid amount for channelclose!");
- else if ( param1 > CHANNELS_MAXPAYMENTS)
- return eval->Invalid("too many payment increments!");
- else if (myGetTransaction(tx.vin[1].prevout.hash,prevTx,hashblock) != 0)
- {
- if ((numvouts=prevTx.vout.size()) > 0 && DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, p1, p2, p3) == 0)
- return eval->Invalid("invalid previous tx OP_RETURN data!");
- else if ((*cp->ismyvin)(tx.vin[tx.vin.size()-1].scriptSig) == 0 || prevTx.vout[tx.vin[tx.vin.size()-1].prevout.n].nValue!=CC_MARKER_VALUE)
- return eval->Invalid("vin."+std::to_string(tx.vin.size()-1)+" is CC marker or invalid marker amount for channelclose!");
- else if (tx.vout[0].nValue != prevTx.vout[0].nValue)
- return eval->Invalid("invalid CC amount, amount must match funds in channel");
- }
- break;
- case 'R':
- //vin.0: normal input
- //vin.1: CC input from channel funding
- //vin.2: CC input from src marker
- //vout.0: CC vout marker to senders pubKey
- //vout.1: CC vout marker to receiver pubKey
- //vout.2: normal output of CC input to senders pubkey
- //vout.n-2: normal change
- //vout.n-1: opreturn - 'R' opentxid senderspubkey receiverspubkey numpayments payment closetxid
- if ( IsCCInput(channelOpenTx.vin[0].scriptSig) != 0 )
- return eval->Invalid("vin.0 is normal for channelopen!");
- else if ( ConstrainVout(channelOpenTx.vout[0],1,channeladdress,numpayments*payment)==0 )
- return eval->Invalid("vout.0 is CC or invalid amount for channelopen!");
- else if ( ConstrainVout(channelOpenTx.vout[1],1,srcmarker,CC_MARKER_VALUE)==0 )
- return eval->Invalid("vout.1 is CC marker to srcpub or invalid amount for channelopen!");
- else if ( ConstrainVout(channelOpenTx.vout[2],1,destmarker,CC_MARKER_VALUE)==0 )
- return eval->Invalid("vout.2 is CC marker to destpub or invalid amount for channelopen!");
- else if (hush_txnotarizedconfirmed(opentxid) == 0)
- return eval->Invalid("channelopen is not yet confirmed(notarized)!");
- else if (hush_txnotarizedconfirmed(param3) == 0)
- return eval->Invalid("channelClose is not yet confirmed(notarized)!");
- else if ( IsCCInput(tx.vin[0].scriptSig) != 0 )
- return eval->Invalid("vin.0 is normal for channelrefund!");
- else if ( IsCCInput(tx.vin[tx.vin.size()-2].scriptSig) == 0 )
- return eval->Invalid("vin."+std::to_string(tx.vin.size()-2)+" CC for channelrefund!");
- else if ( ConstrainVout(tx.vout[0],1,srcmarker,CC_MARKER_VALUE)==0 )
- return eval->Invalid("vout.0 is CC marker to srcpub or invalid amount for channelrefund!");
- else if ( ConstrainVout(tx.vout[1],1,destmarker,CC_MARKER_VALUE)==0 )
- return eval->Invalid("vout.1 is CC marker to destpub or invalid amount for channelrefund!");
- else if ( tokenid!=zeroid && ConstrainVout(tx.vout[2],1,srctokensaddr,param1*payment)==0 )
- return eval->Invalid("vout.2 is CC or invalid amount or invalid receiver for channelrefund!");
- else if ( tokenid==zeroid && ConstrainVout(tx.vout[2],0,srcaddr,param1*payment)==0 )
- return eval->Invalid("vout.2 is normal or invalid amount or invalid receiver for channelrefund!");
- else if ( param1 > CHANNELS_MAXPAYMENTS)
- return eval->Invalid("too many payment increments!");
- else if (myGetTransaction(tx.vin[1].prevout.hash,prevTx,hashblock) != 0)
- {
- if ((numvouts=prevTx.vout.size()) > 0 && DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, p1, p2, p3) == 0)
- return eval->Invalid("invalid previous tx OP_RETURN data!");
- else if ((*cp->ismyvin)(tx.vin[tx.vin.size()-1].scriptSig) == 0 || prevTx.vout[tx.vin[tx.vin.size()-1].prevout.n].nValue!=CC_MARKER_VALUE)
- return eval->Invalid("vin."+std::to_string(tx.vin.size()-1)+" is CC marker or invalid marker amount for channelrefund!");
- else if (tx.vout[2].nValue != prevTx.vout[0].nValue)
- return eval->Invalid("invalid amount, refund amount and funds in channel must match!");
- }
- break;
- default:
- fprintf(stderr,"illegal channels funcid.(%c)\n",funcid);
- return eval->Invalid("unexpected channels funcid");
- }
- }
- else return eval->Invalid("unexpected channels missing funcid");
- retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts);
- if ( retval != 0 )
- LOGSTREAM("channels",CCLOG_INFO, stream << "Channels tx validated" << std::endl);
- else fprintf(stderr,"Channels tx invalid\n");
- return(retval);
- }
- }
-}
-// end of consensus code
-
-// helper functions for rpc calls in rpcwallet.cpp
-
-int64_t AddChannelsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx, CTransaction openTx, uint256 &prevtxid, CPubKey mypk)
-{
- char coinaddr[65]; int64_t param2,totalinputs = 0,numvouts; uint256 txid=zeroid,tmp_txid,hashBlock,param3,tokenid; CTransaction tx; int32_t marker,param1;
- std::vector > unspentOutputs;
- CPubKey srcpub,destpub;
- uint8_t myprivkey[32];
-
- if ((numvouts=openTx.vout.size()) > 0 && DecodeChannelsOpRet(openTx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3)=='O')
- {
- if (tokenid!=zeroid) GetTokensCCaddress1of2(cp,coinaddr,srcpub,destpub);
- else GetCCaddress1of2(cp,coinaddr,srcpub,destpub);
- SetCCunspents(unspentOutputs,coinaddr,true);
- }
- else
- {
- LOGSTREAM("channelscc",CCLOG_INFO, stream << "invalid channel open txid" << std::endl);
- return 0;
- }
- if (srcpub==mypk) marker=1;
- else marker=2;
- for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
- {
- if ( (int32_t)it->first.index==0 && myGetTransaction(it->first.txhash,tx,hashBlock) != 0 && (numvouts=tx.vout.size()) > 0)
- {
- if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3)!=0 &&
- (tmp_txid==openTx.GetHash() || tx.GetHash()==openTx.GetHash()) && IsChannelsMarkervout(cp,tx,marker==1?srcpub:destpub,marker)>0 &&
- (totalinputs=IsChannelsvout(cp,tx,srcpub,destpub,0))>0)
- {
- txid = it->first.txhash;
- break;
- }
- }
- }
- if (txid!=zeroid && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,0) != 0)
- {
- txid=zeroid;
- int32_t mindepth=CHANNELS_MAXPAYMENTS;
- std::vector tmp_txs;
- myGet_mempool_txs(tmp_txs,EVAL_CHANNELS,'P');
- for (std::vector::const_iterator it=tmp_txs.begin(); it!=tmp_txs.end(); it++)
- {
- const CTransaction &txmempool = *it;
- const uint256 &hash = txmempool.GetHash();
-
- if ((numvouts=txmempool.vout.size()) > 0 && DecodeChannelsOpRet(txmempool.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3)=='P' &&
- tmp_txid==openTx.GetHash() && param1 < mindepth)
- {
- txid=hash;
- totalinputs=txmempool.vout[0].nValue;
- mindepth=param1;
- }
- }
- }
- if (txid != zeroid)
- {
- prevtxid=txid;
- mtx.vin.push_back(CTxIn(txid,0,CScript()));
- mtx.vin.push_back(CTxIn(txid,marker,CScript()));
- Myprivkey(myprivkey);
- if (tokenid!=zeroid) CCaddrTokens1of2set(cp,srcpub,destpub,myprivkey,coinaddr);
- else CCaddr1of2set(cp,srcpub,destpub,myprivkey,coinaddr);
- memset(myprivkey,0,32);
- return totalinputs;
- }
- else return 0;
-}
-
-UniValue ChannelOpen(const CPubKey& pk, uint64_t txfee,CPubKey destpub,int32_t numpayments,int64_t payment, uint256 tokenid)
-{
- CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
- uint8_t hash[32],hashdest[32]; uint64_t amount,tokens=0,funds; int32_t i; uint256 hashchain,entropy,hentropy;
- CPubKey mypk; struct CCcontract_info *cp,*cpTokens,C,CTokens;
-
- if ( numpayments <= 0 || payment <= 0 || numpayments > CHANNELS_MAXPAYMENTS )
- CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid ChannelOpen param numpayments." << numpayments << " payment." << payment << " - max_numpayments." << CHANNELS_MAXPAYMENTS);
- if (!destpub.IsFullyValid())
- CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid destination pubkey");
- if (numpayments <1)
- CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid number of payments, must be greater than 0");
- if (payment <1)
- CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid payment amount, must be greater than 0");
- cp = CCinit(&C,EVAL_CHANNELS);
- cpTokens = CCinit(&CTokens,EVAL_TOKENS);
- if ( txfee == 0 )
- txfee = 10000;
- mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey());
- funds = numpayments * payment;
- if (tokenid!=zeroid)
- {
- amount=AddNormalinputs(mtx,mypk,txfee+2*CC_MARKER_VALUE,5,pk.IsValid());
- tokens=AddTokenCCInputs(cpTokens, mtx, mypk, tokenid, funds, 64);
- }
- else amount=AddNormalinputs(mtx,mypk,funds+txfee+2*CC_MARKER_VALUE,64,pk.IsValid());
- if (amount+tokens >= funds+txfee+2*CC_MARKER_VALUE)
- {
- hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash,mtx.vin[0].prevout.n,1);
- endiancpy(hash,(uint8_t *)&hentropy,32);
- for (i=0; ifunds) mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS,tokens-funds,mypk));
- return(FinalizeCCTxExt(pk.IsValid(),0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('O',tokenid,zeroid,mypk,destpub,numpayments,payment,hashchain)));
- }
- CCERR_RESULT("channelscc",CCLOG_INFO, stream << "error adding funds");
-}
-
-UniValue ChannelPayment(const CPubKey& pk, uint64_t txfee,uint256 opentxid,int64_t amount, uint256 secret)
-{
- CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
- CPubKey mypk,srcpub,destpub; uint256 txid,hashchain,gensecret,hashblock,entropy,hentropy,prevtxid,param3,tokenid;
- struct CCcontract_info *cp,C; int32_t i,funcid,prevdepth,numvouts,numpayments,totalnumpayments;
- int64_t payment,change,funds,param2;
- uint8_t hash[32],hashdest[32];
- CTransaction channelOpenTx,prevTx;
-
- cp = CCinit(&C,EVAL_CHANNELS);
- if ( txfee == 0 )
- txfee = 10000;
- mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey());
- if (amount <1)
- CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid payment amount, must be greater than 0");
- if (myGetTransaction(opentxid,channelOpenTx,hashblock) == 0)
- CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid channel open txid");
- if ((numvouts=channelOpenTx.vout.size()) > 0 && DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey, tokenid, txid, srcpub, destpub, totalnumpayments, payment, hashchain)=='O')
- {
- if (mypk != srcpub && mypk != destpub)
- CCERR_RESULT("channelscc",CCLOG_INFO, stream << "this is not our channel");
- else if (amount % payment != 0 || amount 0)
- {
- if ((funds=AddChannelsInputs(cp,mtx,channelOpenTx,prevtxid,mypk)) !=0 && (change=funds-amount)>=0)
- {
- numpayments=amount/payment;
- if (myGetTransaction(prevtxid,prevTx,hashblock) != 0 && (numvouts=prevTx.vout.size()) > 0 &&
- ((funcid = DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, txid, srcpub, destpub, prevdepth, param2, param3)) != 0) &&
- (funcid == 'P' || funcid=='O'))
- {
- if (numpayments > prevdepth)
- CCERR_RESULT("channelscc",CCLOG_INFO, stream << "not enough funds in channel for that amount");
- else if (numpayments == 0)
- CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid amount");
- if (secret!=zeroid)
- {
- endiancpy(hash, (uint8_t * ) & secret, 32);
- for (i = 0; i < totalnumpayments-(prevdepth-numpayments); i++)
- {
- vcalc_sha256(0, hashdest, hash, 32);
- memcpy(hash, hashdest, 32);
- }
- endiancpy((uint8_t * ) & gensecret, hashdest, 32);
- if (gensecret!=hashchain) CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid secret supplied");
- }
- else
- {
- hentropy = DiceHashEntropy(entropy,channelOpenTx.vin[0].prevout.hash,channelOpenTx.vin[0].prevout.n,1);
- if (prevdepth-numpayments)
- {
- endiancpy(hash, (uint8_t * ) & hentropy, 32);
- for (i = 0; i < prevdepth-numpayments; i++)
- {
- vcalc_sha256(0, hashdest, hash, 32);
- memcpy(hash, hashdest, 32);
- }
- endiancpy((uint8_t * ) & secret, hashdest, 32);
- }
- else endiancpy((uint8_t * ) & secret, (uint8_t * ) & hentropy, 32);
- }
- }
- else
- CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid previous tx");
- if (tokenid!=zeroid) mtx.vout.push_back(MakeTokensCC1of2vout(EVAL_CHANNELS, change, srcpub, destpub));
- else mtx.vout.push_back(MakeCC1of2vout(EVAL_CHANNELS, change, srcpub, destpub));
- mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,srcpub));
- mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,destpub));
- if (tokenid!=zeroid) mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, amount, destpub));
- else mtx.vout.push_back(CTxOut(amount, CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG));
- return (FinalizeCCTxExt(pk.IsValid(), 0, cp, mtx, mypk, txfee, EncodeChannelsOpRet('P', tokenid, opentxid, srcpub, destpub, prevdepth-numpayments, numpayments, secret)));
- }
- else
- CCERR_RESULT("channelscc",CCLOG_INFO, stream << "error adding CC inputs");
- }
- CCERR_RESULT("channelscc",CCLOG_INFO, stream << "error adding normal inputs");
-}
-
-UniValue ChannelClose(const CPubKey& pk, uint64_t txfee,uint256 opentxid)
-{
- CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
- CPubKey mypk,srcpub,destpub; struct CCcontract_info *cp,C;
- CTransaction channelOpenTx;
- uint256 hashblock,tmp_txid,prevtxid,hashchain,tokenid;
- int32_t numvouts,numpayments;
- int64_t payment,funds;
-
- // verify this is one of our outbound channels
- cp = CCinit(&C,EVAL_CHANNELS);
- if ( txfee == 0 )
- txfee = 10000;
- mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey());
- if (myGetTransaction(opentxid,channelOpenTx,hashblock) == 0)
- CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid channel open txid");
- if ((numvouts=channelOpenTx.vout.size()) < 1 || DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,numpayments,payment,hashchain)!='O')
- CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid channel open tx");
- if (hush_txnotarizedconfirmed(opentxid)==false)
- CCERR_RESULT("channelscc",CCLOG_INFO, stream <<"channelsopen tx not yet confirmed/notarized");
- if (mypk != srcpub)
- CCERR_RESULT("channelscc",CCLOG_INFO, stream << "cannot close, you are not channel owner");
- if ( AddNormalinputs(mtx,mypk,txfee+CC_MARKER_VALUE,3,pk.IsValid()) > 0 )
- {
- if ((funds=AddChannelsInputs(cp,mtx,channelOpenTx,prevtxid,mypk)) !=0 && funds>0)
- {
- if (tokenid!=zeroid) mtx.vout.push_back(MakeTokensCC1of2vout(EVAL_CHANNELS, funds, mypk, destpub));
- else mtx.vout.push_back(MakeCC1of2vout(EVAL_CHANNELS, funds, mypk, destpub));
- mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,mypk));
- mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,destpub));
- return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('C',tokenid,opentxid,mypk,destpub,funds/payment,payment,zeroid)));
- }
- else
- CCERR_RESULT("channelscc",CCLOG_INFO, stream << "error adding CC inputs");
- }
- CCERR_RESULT("channelscc",CCLOG_INFO, stream << "error adding normal inputs");
-}
-
-UniValue ChannelRefund(const CPubKey& pk, uint64_t txfee,uint256 opentxid,uint256 closetxid)
-{
- CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
- CPubKey mypk; struct CCcontract_info *cp,C; int64_t funds,payment,param2;
- int32_t i,numpayments,numvouts,param1;
- uint256 hashchain,hashblock,txid,prevtxid,param3,tokenid;
- CTransaction channelOpenTx,channelCloseTx,prevTx;
- CPubKey srcpub,destpub;
-
- // verify stoptxid and origtxid match and are mine
- cp = CCinit(&C,EVAL_CHANNELS);
- if ( txfee == 0 )
- txfee = 10000;
- mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey());
- if (myGetTransaction(closetxid,channelCloseTx,hashblock) == 0)
- CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid channel close txid");
- if ((numvouts=channelCloseTx.vout.size()) < 1 || DecodeChannelsOpRet(channelCloseTx.vout[numvouts-1].scriptPubKey,tokenid,txid,srcpub,destpub,param1,param2,param3)!='C')
- CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid channel close tx");
- if (hush_txnotarizedconfirmed(closetxid)==false)
- CCERR_RESULT("channelscc",CCLOG_INFO, stream << "channelsclose tx not yet confirmed/notarized");
- if (txid!=opentxid)
- CCERR_RESULT("channelscc",CCLOG_INFO, stream << "open and close txid are not from same channel");
- if (myGetTransaction(opentxid,channelOpenTx,hashblock) == 0)
- CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid channel open txid");
- if (hush_txnotarizedconfirmed(opentxid)==false)
- CCERR_RESULT("channelscc",CCLOG_INFO, stream << "channelsopen tx not yet confirmed/notarized");
- if ((numvouts=channelOpenTx.vout.size()) < 1 || DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey,tokenid,txid,srcpub,destpub,numpayments,payment,hashchain)!='O')
- CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid channel open tx");
- if (mypk != srcpub)
- CCERR_RESULT("channelscc",CCLOG_INFO, stream << "cannot refund, you are not the channel owner");
- if ( AddNormalinputs(mtx,mypk,txfee+CC_MARKER_VALUE,3,pk.IsValid()) > 0 )
- {
- if ((funds=AddChannelsInputs(cp,mtx,channelOpenTx,prevtxid,mypk)) !=0 && funds>0)
- {
- if ((myGetTransaction(prevtxid,prevTx,hashblock) != 0) && (numvouts=prevTx.vout.size()) > 0 &&
- DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, txid, srcpub, destpub, param1, param2, param3) != 0)
- {
- mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,mypk));
- mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,destpub));
- if (tokenid!=zeroid) mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS,funds,mypk));
- else mtx.vout.push_back(CTxOut(funds,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
- return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('R',tokenid,opentxid,mypk,destpub,funds/payment,payment,closetxid)));
- }
- else
- CCERR_RESULT("channelscc",CCLOG_INFO, stream << "previous tx is invalid");
- }
- else
- CCERR_RESULT("channelscc",CCLOG_INFO, stream << "error adding CC inputs");
- }
- CCERR_RESULT("channelscc",CCLOG_INFO, stream << "error adding normal inputs");
-}
-
-UniValue ChannelsList(const CPubKey& pk)
-{
- UniValue result(UniValue::VOBJ); std::vector txids; struct CCcontract_info *cp,C; uint256 txid,hashBlock,tmp_txid,param3,tokenid;
- CTransaction tx; char myCCaddr[65],str[512],pub[34]; CPubKey mypk,srcpub,destpub; int32_t vout,numvouts,param1;
- int64_t nValue,param2;
-
- cp = CCinit(&C,EVAL_CHANNELS);
- mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey());
- GetCCaddress(cp,myCCaddr,mypk);
- SetCCtxids(txids,myCCaddr,true,EVAL_CHANNELS,zeroid,'O');
- result.push_back(Pair("result","success"));
- result.push_back(Pair("name","Channels List"));
- for (std::vector::const_iterator it=txids.begin(); it!=txids.end(); it++)
- {
- txid = *it;
- if ( myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 0 )
- {
- if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3) == 'O')
- {
- sprintf(str,"%lld payments of %lld satoshi to %s",(long long)param1,(long long)param2,pubkey33_str(pub,(uint8_t *)&destpub));
- result.push_back(Pair(txid.GetHex().data(),str));
- }
- }
- }
- return(result);
-}
-
-UniValue ChannelsInfo(const CPubKey& pk,uint256 channeltxid)
-{
- UniValue result(UniValue::VOBJ),array(UniValue::VARR); CTransaction tx,opentx; uint256 txid,tmp_txid,hashBlock,param3,opentxid,hashchain,tokenid;
- struct CCcontract_info *cp,C; char CCaddr[65],addr[65],str[512]; int32_t vout,numvouts,param1,numpayments;
- int64_t param2,payment; CPubKey srcpub,destpub,mypk;
- std::vector txids; std::vector txs;
-
- cp = CCinit(&C,EVAL_CHANNELS);
- mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey());
-
- if (myGetTransaction(channeltxid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 0 &&
- (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,opentxid,srcpub,destpub,param1,param2,param3) == 'O'))
- {
- GetCCaddress1of2(cp,CCaddr,srcpub,destpub);
- Getscriptaddress(addr,CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG);
- result.push_back(Pair("result","success"));
- result.push_back(Pair("Channel CC address",CCaddr));
- result.push_back(Pair("Destination address",addr));
- result.push_back(Pair("Number of payments",param1));
- if(tokenid!=zeroid)
- {
- result.push_back(Pair("Token id",tokenid.GetHex().data()));
- result.push_back(Pair("Denomination (token satoshi)",i64tostr(param2)));
- result.push_back(Pair("Amount (token satoshi)",i64tostr(param1*param2)));
- }
- else
- {
- result.push_back(Pair("Denomination (satoshi)",i64tostr(param2)));
- result.push_back(Pair("Amount (satoshi)",i64tostr(param1*param2)));
- }
- GetCCaddress(cp,CCaddr,mypk);
- SetCCtxids(txids,CCaddr,true,EVAL_CHANNELS,channeltxid,0);
- for (std::vector::const_iterator it=txids.begin(); it!=txids.end(); it++)
- {
- if (myGetTransaction(*it,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 0 &&
- DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3)!=0 && (tmp_txid==channeltxid || tx.GetHash()==channeltxid))
- txs.push_back(tx);
- }
- std::vector tmp_txs;
- myGet_mempool_txs(tmp_txs,EVAL_CHANNELS,'P');
- for (std::vector::const_iterator it=tmp_txs.begin(); it!=tmp_txs.end(); it++)
- {
- const CTransaction &txmempool = *it;
-
- if ((numvouts=txmempool.vout.size()) > 0 && DecodeChannelsOpRet(txmempool.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3) == 'P' && tmp_txid==channeltxid)
- txs.push_back(txmempool);
- }
- for (std::vector::const_iterator it=txs.begin(); it!=txs.end(); it++)
- {
- tx=*it;
- if ((numvouts= tx.vout.size()) > 0 )
- {
- UniValue obj(UniValue::VOBJ);
- if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3) == 'O' && tx.GetHash()==channeltxid)
- {
- obj.push_back(Pair("Open",tx.GetHash().GetHex().data()));
- }
- else if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,opentxid,srcpub,destpub,param1,param2,param3) == 'P' && opentxid==channeltxid)
- {
- if (myGetTransaction(opentxid,opentx,hashBlock) != 0 && (numvouts=opentx.vout.size()) > 0 &&
- DecodeChannelsOpRet(opentx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,numpayments,payment,hashchain) == 'O')
- {
- Getscriptaddress(str,tx.vout[3].scriptPubKey);
- obj.push_back(Pair("Payment",tx.GetHash().GetHex().data()));
- obj.push_back(Pair("Number of payments",param2));
- obj.push_back(Pair("Amount",param2*payment));
- obj.push_back(Pair("Destination",str));
- obj.push_back(Pair("Secret",param3.ToString().c_str()));
- obj.push_back(Pair("Payments left",param1));
- }
- }
- else if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,opentxid,srcpub,destpub,param1,param2,param3) == 'C' && opentxid==channeltxid)
- {
- obj.push_back(Pair("Close",tx.GetHash().GetHex().data()));
- }
- else if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,opentxid,srcpub,destpub,param1,param2,param3) == 'R' && opentxid==channeltxid)
- {
- Getscriptaddress(str,tx.vout[2].scriptPubKey);
- obj.push_back(Pair("Refund",tx.GetHash().GetHex().data()));
- obj.push_back(Pair("Amount",param1*param2));
- obj.push_back(Pair("Destination",str));
- }
- array.push_back(obj);
- }
- }
- result.push_back(Pair("Transactions",array));
- }
- else
- {
- result.push_back(Pair("result","error"));
- result.push_back(Pair("Error","Channel not found!"));
- }
- return(result);
-}
diff --git a/src/cc/dice.cpp b/src/cc/dice.cpp
deleted file mode 100644
index 6b0d06075..000000000
--- a/src/cc/dice.cpp
+++ /dev/null
@@ -1,1871 +0,0 @@
-// Copyright (c) 2016-2023 The Hush developers
-// Distributed under the GPLv3 software license, see the accompanying
-// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
-/******************************************************************************
- * Copyright © 2014-2019 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 "CCdice.h"
-
-// timeout
-
-/*
- in order to implement a dice game, we need a source of entropy, reasonably fast completion time and a way to manage the utxos.
-
- 1. CC vout locks "house" funds with hash(entropy)
- 2. bettor submits bet, with entropy, odds, houseid and sends combined amount into another CC vout.
- 3. house account sends funds to winner/loser with proof of entropy
- 4. if timeout, bettor gets refund
-
- 2. and 3. can be done in mempool
-
- The house commits to an entropy value by including the hash of the entropy value in the 'E' transaction.
-
- To bet, one of these 'E' transactions is used as the first input and its hashed entropy is combined with the unhashed entropy attached to the bet 'B' transaction.
-
- The house node monitors the 'B' transactions and if it sees one of its own, it creates either a winner 'W' or loser 'L' transaction, with proof of hash of entropy.
-
- In the even the house node doesnt respond before timeoutblocks, then anybody (including bettor) can undo the bet with funds going back to the house and bettor
-
- In order for people to play dice, someone (anyone) needs to create a funded dice plan and addfunding with enough utxo to allow players to find one.
-
-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.
-
- Actually a much better solution to this is possible, which allows to retain the realtime response aspect of dice CC, which is critical to its usage.
-
-What is needed is for the dealer node to track the entropy tx that was already broadcast into the mempool with its entropy revealed. Then before processing a dicebet, it is checked against the already revealed list. If it is found, the dicebet is refunded with proof that a different dicebet was already used to reveal the entropy
-
- change to hashtables
- validate refund
-
- */
-
-#include "../compat/endian.h"
-
-#define MAX_ENTROPYUSED 8192
-#define DICE_MINUTXOS 15000
-extern int32_t HUSH_INSYNC;
-
-pthread_mutex_t DICE_MUTEX,DICEREVEALED_MUTEX;
-
-struct dicefinish_utxo { uint256 txid; int32_t vout; };
-
-struct dicefinish_info
-{
- struct dicefinish_info *prev,*next;
- CTransaction betTx;
- uint256 fundingtxid,bettxid,entropyused,txid;
- uint64_t sbits;
- int64_t winamount;
- int32_t iswin,entropyvout,orphaned;
- uint32_t bettxid_ready,revealed;
- std::string rawtx;
- uint8_t funcid;
-} *DICEFINISH_LIST;
-
-struct dicehash_entry
-{
- UT_hash_handle hh;
- uint256 bettxid;
-} *DICEHASH_TABLE;
-
-struct dice_entropy
-{
- UT_hash_handle hh;
- uint256 entropyused,bettxid;
- CTransaction betTx;
- int32_t entropyvout;
-} *DICE_ENTROPY;
-
-struct dicehash_entry *_dicehash_find(uint256 bettxid)
-{
- struct dicehash_entry *ptr;
- HASH_FIND(hh,DICEHASH_TABLE,&bettxid,sizeof(bettxid),ptr);
- return(ptr);
-}
-
-int32_t _dicehash_clear(uint256 bettxid)
-{
- struct dicehash_entry *ptr;
- HASH_FIND(hh,DICEHASH_TABLE,&bettxid,sizeof(bettxid),ptr);
- if ( ptr != 0 )
- {
- fprintf(stderr,"delete %s\n",bettxid.GetHex().c_str());
- HASH_DELETE(hh,DICEHASH_TABLE,ptr);
- return(0);
- } else fprintf(stderr,"hashdelete couldnt find %s\n",bettxid.GetHex().c_str());
- return(-1);
-}
-
-struct dicehash_entry *_dicehash_add(uint256 bettxid)
-{
- struct dicehash_entry *ptr;
- ptr = (struct dicehash_entry *)calloc(1,sizeof(*ptr));
- ptr->bettxid = bettxid;
- HASH_ADD(hh,DICEHASH_TABLE,bettxid,sizeof(bettxid),ptr);
- return(ptr);
-}
-
-int32_t _dicerevealed_find(uint256 &oldbettxid,CTransaction &oldbetTx,int32_t &oldentropyvout,uint256 entropyused,uint256 bettxid,int32_t entropyvout)
-{
- struct dice_entropy *ptr;
- HASH_FIND(hh,DICE_ENTROPY,&entropyused,sizeof(entropyused),ptr);
- if ( ptr != 0 )
- {
- if ( entropyvout == ptr->entropyvout )
- {
- if ( bettxid == ptr->bettxid )
- {
- //fprintf(stderr,"identical %s E.%s v.%d\n",bettxid.GetHex().c_str(),entropyused.GetHex().c_str(),entropyvout);
- return(entropyvout+1);
- }
- else
- {
- fprintf(stderr,"found identical entropy used.%s %s vs %s v.%d vs %d\n",entropyused.GetHex().c_str(),bettxid.GetHex().c_str(),ptr->bettxid.GetHex().c_str(),entropyvout,ptr->entropyvout);
- oldbettxid = ptr->bettxid;
- oldbetTx = ptr->betTx;
- oldentropyvout = ptr->entropyvout;
- return(-1);
- }
- } else fprintf(stderr,"shared entropy.%s vouts %d vs %d\n",entropyused.GetHex().c_str(),entropyvout,ptr->entropyvout);
- }
- return(0);
-}
-
-struct dice_entropy *_dicerevealed_add(uint256 entropyused,uint256 bettxid,CTransaction betTx,int32_t entropyvout)
-{
- struct dice_entropy *ptr;
- ptr = (struct dice_entropy *)calloc(1,sizeof(*ptr));
- ptr->entropyused = entropyused;
- ptr->bettxid = bettxid;
- ptr->betTx = betTx;
- ptr->entropyvout = entropyvout;
- HASH_ADD(hh,DICE_ENTROPY,entropyused,sizeof(entropyused),ptr);
- return(ptr);
-}
-
-int32_t DiceEntropyUsed(CTransaction &oldbetTx,uint256 &oldbettxid,int32_t &oldentropyvout,uint256 entropyused,uint256 bettxid,CTransaction betTx,int32_t entropyvout)
-{
- int32_t retval;
- oldbettxid = zeroid;
- if ( entropyused == zeroid || bettxid == zeroid )
- {
- fprintf(stderr,"null entropyused or bettxid\n");
- return(1);
- }
- pthread_mutex_lock(&DICEREVEALED_MUTEX);
- retval = _dicerevealed_find(oldbettxid,oldbetTx,oldentropyvout,entropyused,bettxid,entropyvout);
- pthread_mutex_unlock(&DICEREVEALED_MUTEX);
- return(retval);
-}
-
-bool mySenddicetransaction(std::string res,uint256 entropyused,int32_t entropyvout,uint256 bettxid,CTransaction betTx,uint8_t funcid,struct dicefinish_info *ptr)
-{
- CTransaction tx; int32_t i=0,retval=-1,oldentropyvout; uint256 oldbettxid; CTransaction oldbetTx;
- if ( res.empty() == 0 && res.size() > 64 && is_hexstr((char *)res.c_str(),0) > 64 )
- {
- if ( DecodeHexTx(tx,res) != 0 )
- {
- if ( ptr != 0 )
- ptr->txid = tx.GetHash();
- //fprintf(stderr,"%s\n%s\n",res.c_str(),uint256_str(str,tx.GetHash()));
- if ( funcid == 'R' || (retval= DiceEntropyUsed(oldbetTx,oldbettxid,oldentropyvout,entropyused,bettxid,betTx,entropyvout)) >= 0 )
- {
- LOCK(cs_main);
- if ( myAddtomempool(tx) != 0 )
- {
- RelayTransaction(tx);
- if ( retval == 0 )
- {
- if ( ptr != 0 )
- ptr->revealed = (uint32_t)time(NULL);
- pthread_mutex_lock(&DICEREVEALED_MUTEX);
- _dicerevealed_add(entropyused,bettxid,betTx,entropyvout);
- pthread_mutex_unlock(&DICEREVEALED_MUTEX);
- fprintf(stderr,"added.%c to mempool.[%d] and broadcast entropyused.%s bettxid.%s -> %s\n",funcid,i,entropyused.GetHex().c_str(),bettxid.GetHex().c_str(),tx.GetHash().GetHex().c_str());
- } else fprintf(stderr,"rebroadcast.%c to mempool.[%d] and broadcast entropyused.%s bettxid.%s -> %s\n",funcid,i,entropyused.GetHex().c_str(),bettxid.GetHex().c_str(),tx.GetHash().GetHex().c_str());
- return(true);
- }
- else
- {
- RelayTransaction(tx);
- fprintf(stderr,"rebroadcast.%c and clear [%d] and broadcast entropyused.%s bettxid.%s -> %s\n",funcid,i,entropyused.GetHex().c_str(),bettxid.GetHex().c_str(),tx.GetHash().GetHex().c_str());
- if ( ptr != 0 )
- {
- if ( ptr->rawtx.empty() == 0 )
- ptr->rawtx.clear();
- ptr->txid = zeroid;
- }
- //fprintf(stderr,"error adding funcid.%c E.%s bet.%s -> %s to mempool, probably Disable replacement feature size.%d\n",funcid,entropyused.GetHex().c_str(),bettxid.GetHex().c_str(),tx.GetHash().GetHex().c_str(),(int32_t)ptr->rawtx.size());
- }
- } else fprintf(stderr,"error duplicate entropyused different bettxid\n");
- } else fprintf(stderr,"error decoding hex\n");
- }
- return(false);
-}
-
-int32_t dicefinish_utxosget(int32_t &total,struct dicefinish_utxo *utxos,int32_t max,char *coinaddr)
-{
- int32_t n = 0; int64_t threshold = 2 * 10000;
- total = 0;
- std::vector > unspentOutputs;
- SetCCunspents(unspentOutputs,coinaddr,false);
- {
- LOCK(mempool.cs);
- for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
- {
- if ( myIsutxo_spentinmempool(ignoretxid,ignorevin,it->first.txhash,(int32_t)it->first.index) == 0 )
- {
- if ( it->second.satoshis < threshold || it->second.satoshis > 10*threshold )
- continue;
- total++;
- if ( n < max )
- {
- if ( utxos != 0 )
- {
- utxos[n].txid = it->first.txhash;
- utxos[n].vout = (int32_t)it->first.index;
- }
- n++;
- }
- }
- }
- }
- total -= n;
- return(n);
-}
-
-int32_t dice_betspent(char *debugstr,uint256 bettxid)
-{
- CSpentIndexValue value,value2;
- CSpentIndexKey key(bettxid,0);
- CSpentIndexKey key2(bettxid,1);
- if ( GetSpentIndex(key,value) != 0 || GetSpentIndex(key2,value2) != 0 )
- {
- //fprintf(stderr,"%s txid.%s already spent\n",debugstr,bettxid.GetHex().c_str());
- return(1);
- }
- {
- //LOCK(mempool.cs);
- if ( myIsutxo_spentinmempool(ignoretxid,ignorevin,bettxid,0) != 0 || myIsutxo_spentinmempool(ignoretxid,ignorevin,bettxid,1) != 0 )
- {
- fprintf(stderr,"%s bettxid.%s already spent in mempool\n",debugstr,bettxid.GetHex().c_str());
- return(-1);
- }
- }
- return(0);
-}
-
-void dicefinish_delete(struct dicefinish_info *ptr)
-{
- pthread_mutex_lock(&DICE_MUTEX);
- _dicehash_clear(ptr->bettxid);
- pthread_mutex_unlock(&DICE_MUTEX);
- DL_DELETE(DICEFINISH_LIST,ptr);
- free(ptr);
-}
-
-void *dicefinish(void *_ptr)
-{
- std::vector mypk; struct CCcontract_info *cp,C; char name[32],coinaddr[64],CCaddr[64]; std::string res; int32_t newht,newblock,entropyvout,numblocks,lastheight=0,vin0_needed,i,n,m,num,iter,result; struct dicefinish_info *ptr,*tmp; uint32_t now; struct dicefinish_utxo *utxos; uint256 hashBlock,entropyused; CPubKey dicepk; CTransaction betTx,finishTx,tx;
- mypk = Mypubkey();
- pubkey2addr(coinaddr,mypk.data());
- cp = CCinit(&C,EVAL_DICE);
- dicepk = GetUnspendable(cp,0);
- GetCCaddress(cp,CCaddr,GetUnspendable(cp,0));
- fprintf(stderr,"start dicefinish thread %s CCaddr.%s\n",coinaddr,CCaddr);
- if ( (newht= HUSH_INSYNC) == 0 )
- sleep(7);
- sleep(3);
- while ( 1 )
- {
- if ( newht != 0 && lastheight != newht )
- {
- lastheight = newht;
- newblock = 1;
- fprintf(stderr,"dicefinish process lastheight.%d <- newht.%d\n",lastheight,newht);
- } else newblock = 0;
- now = (uint32_t)time(NULL);
- for (iter=-1; iter<=1; iter+=2)
- {
- vin0_needed = 0;
- DL_FOREACH_SAFE(DICEFINISH_LIST,ptr,tmp)
- {
- if ( ptr->iswin != iter )
- continue;
- if ( ptr->revealed != 0 && now > ptr->revealed+3600 )
- {
- fprintf(stderr,"purge %s\n",ptr->bettxid.GetHex().c_str());
- dicefinish_delete(ptr);
- continue;
- }
- if ( ptr->bettxid_ready == 0 )
- {
- if ( myGetTransaction(ptr->bettxid,betTx,hashBlock) != 0 && hashBlock != zeroid )
- ptr->bettxid_ready = (uint32_t)time(NULL);
- else if ( mytxid_inmempool(ptr->bettxid) != 0 )
- ptr->bettxid_ready = (uint32_t)time(NULL);
- }
- else if ( newblock != 0 && (myGetTransaction(ptr->bettxid,betTx,hashBlock) == 0 || now > ptr->bettxid_ready+600) )
- {
- fprintf(stderr,"ORPHANED bettxid.%s\n",ptr->bettxid.GetHex().c_str());
- dicefinish_delete(ptr);
- continue;
- }
- else if ( newblock != 0 && ptr->txid != zeroid )
- {
- if ( myGetTransaction(ptr->txid,finishTx,hashBlock) == 0 )
- {
- ptr->orphaned++;
- fprintf(stderr,"ORPHANED.%d finish txid.%s\n",ptr->orphaned,ptr->txid.GetHex().c_str());
- if ( ptr->orphaned < 4 )
- continue;
- if ( ptr->rawtx.empty() == 0 )
- ptr->rawtx.clear();
- ptr->txid = zeroid;
- unstringbits(name,ptr->sbits);
- result = 0;
- ptr->orphaned = 0;
- res = DiceBetFinish(ptr->funcid,entropyused,entropyvout,&result,0,name,ptr->fundingtxid,ptr->bettxid,ptr->iswin,zeroid,-2);
- if ( ptr->entropyused == zeroid )
- {
- ptr->entropyused = entropyused;
- ptr->entropyvout = entropyvout;
- }
- if ( entropyused != ptr->entropyused || entropyvout != ptr->entropyvout )
- {
- fprintf(stderr,"WARNING entropy %s != %s || %d != %d\n",entropyused.GetHex().c_str(),ptr->entropyused.GetHex().c_str(),entropyvout,ptr->entropyvout);
- }
- if ( result > 0 )
- {
- ptr->rawtx = res;
- fprintf(stderr,"send refund!\n");
- mySenddicetransaction(ptr->rawtx,ptr->entropyused,ptr->entropyvout,ptr->bettxid,ptr->betTx,ptr->funcid,ptr);
- }
- dicefinish_delete(ptr);
- continue;
- }
- }
- if ( ptr->bettxid_ready != 0 )
- {
- if ( now > ptr->bettxid_ready + 2*3600 )
- {
- fprintf(stderr,"purge bettxid_ready %s\n",ptr->bettxid.GetHex().c_str());
- dicefinish_delete(ptr);
- continue;
- }
- else if ( newblock != 0 )
- {
- if ( ptr->txid != zeroid )
- {
- CCduration(numblocks,ptr->txid);
- //fprintf(stderr,"duration finish txid.%s\n",ptr->txid.GetHex().c_str());
- if ( numblocks == 0 )
- mySenddicetransaction(ptr->rawtx,ptr->entropyused,ptr->entropyvout,ptr->bettxid,ptr->betTx,ptr->funcid,ptr);
- else continue;
- }
- }
- if ( ptr->txid == zeroid )
- vin0_needed++;
- }
- }
- if ( vin0_needed > 0 )
- {
- num = 0;
-//fprintf(stderr,"iter.%d vin0_needed.%d\n",iter,vin0_needed);
- utxos = (struct dicefinish_utxo *)calloc(vin0_needed,sizeof(*utxos));
- if ( (n= dicefinish_utxosget(num,utxos,vin0_needed,coinaddr)) > 0 )
- {
-//fprintf(stderr,"iter.%d vin0_needed.%d got %d, num 0.0002 %d\n",iter,vin0_needed,n,num);
- m = 0;
- DL_FOREACH_SAFE(DICEFINISH_LIST,ptr,tmp)
- {
- if ( ptr->iswin != iter )
- continue;
- if ( ptr->revealed != 0 && time(NULL) > ptr->revealed+3600 )
- {
- fprintf(stderr,"purge2 %s\n",ptr->bettxid.GetHex().c_str());
- dicefinish_delete(ptr);
- continue;
- }
- if ( ptr->txid != zeroid )
- {
- CCduration(numblocks,ptr->txid);
- //fprintf(stderr,"numblocks %s %d\n",ptr->txid.GetHex().c_str(),numblocks);
- if ( numblocks > 0 )
- continue;
- }
- if ( ptr->bettxid_ready != 0 && ptr->rawtx.size() == 0 && dice_betspent((char *)"dicefinish",ptr->bettxid) <= 0 && ptr->txid == zeroid )
- {
- unstringbits(name,ptr->sbits);
- result = 0;
- res = DiceBetFinish(ptr->funcid,entropyused,entropyvout,&result,0,name,ptr->fundingtxid,ptr->bettxid,ptr->iswin,utxos[m].txid,utxos[m].vout);
- if ( ptr->entropyused == zeroid )
- {
- ptr->entropyused = entropyused;
- ptr->entropyvout = entropyvout;
- }
- if ( entropyused != ptr->entropyused || entropyvout != ptr->entropyvout )
- {
- fprintf(stderr,"WARNING entropy %s != %s || %d != %d\n",entropyused.GetHex().c_str(),ptr->entropyused.GetHex().c_str(),entropyvout,ptr->entropyvout);
- }
- if ( result > 0 )
- {
- ptr->rawtx = res;
- mySenddicetransaction(ptr->rawtx,ptr->entropyused,ptr->entropyvout,ptr->bettxid,ptr->betTx,ptr->funcid,ptr);
- }
- else if ( result != -2 )
- {
- fprintf(stderr,"error doing the dicefinish %d of %d process %s %s using %s/v%d need %.8f\n",m,n,iter<0?"loss":"win",ptr->bettxid.GetHex().c_str(),utxos[m].txid.GetHex().c_str(),utxos[m].vout,(double)(iter<0 ? 0 : ptr->winamount)/COIN);
- if ( ptr->rawtx.empty() == 0 )
- ptr->rawtx.clear();
- ptr->txid = zeroid;
- }
- if ( ++m >= n )
- break;
- }
- else
- {
- //fprintf(stderr,"error ready.%d dicefinish %d of %d process %s %s using need %.8f finish.%s size.%d betspent.%d\n",ptr->bettxid_ready,m,n,iter<0?"loss":"win",ptr->bettxid.GetHex().c_str(),(double)(iter<0 ? 0 : ptr->winamount)/COIN,ptr->txid.GetHex().c_str(),(int32_t)ptr->rawtx.size(),dice_betspent((char *)"dicefinish",ptr->bettxid));
- }
- }
- } else if ( system("cc/dapps/sendmany100") != 0 )
- fprintf(stderr,"error issing cc/dapps/sendmany100\n");
- free(utxos);
- }
- }
- if ( (newht= HUSH_INSYNC) == 0 || newht == lastheight )
- sleep(3);
- else usleep(500000);
- }
- return(0);
-}
-
-void DiceQueue(int32_t iswin,uint64_t sbits,uint256 fundingtxid,uint256 bettxid,CTransaction betTx,int32_t entropyvout)
-{
- static int32_t didinit;
- struct dicefinish_info *ptr; int32_t i,duplicate=0; uint64_t txfee = 10000;
- if ( didinit == 0 )
- {
- if ( pthread_create((pthread_t *)malloc(sizeof(pthread_t)),NULL,dicefinish,0) == 0 )
- {
- pthread_mutex_init(&DICE_MUTEX,NULL);
- pthread_mutex_init(&DICEREVEALED_MUTEX,NULL);
- didinit = 1;
- }
- else
- {
- fprintf(stderr,"error launching dicefinish thread\n");
- return;
- }
- }
- //if ( dice_betspent((char *)"DiceQueue",bettxid) != 0 )
- // return;
- pthread_mutex_lock(&DICE_MUTEX);
- if ( _dicehash_find(bettxid) == 0 )
- {
- _dicehash_add(bettxid);
- ptr = (struct dicefinish_info *)calloc(1,sizeof(*ptr));
- ptr->fundingtxid = fundingtxid;
- ptr->bettxid = bettxid;
- ptr->betTx = betTx;
- ptr->sbits = sbits;
- ptr->iswin = iswin;
- ptr->winamount = betTx.vout[1].nValue * ((betTx.vout[2].nValue - txfee)+1);
- ptr->entropyvout = entropyvout;
- DL_APPEND(DICEFINISH_LIST,ptr);
- fprintf(stderr,"queued %dx iswin.%d %.8f -> %.8f %s\n",(int32_t)(betTx.vout[2].nValue - txfee),iswin,(double)betTx.vout[1].nValue/COIN,(double)ptr->winamount/COIN,bettxid.GetHex().c_str());
- }
- else
- {
- //fprintf(stderr,"DiceQueue status bettxid.%s already in list\n",bettxid.GetHex().c_str());
- //_dicehash_clear(bettxid);
- }
- pthread_mutex_unlock(&DICE_MUTEX);
-}
-
-CPubKey DiceFundingPk(CScript scriptPubKey)
-{
- CPubKey pk; uint8_t *ptr,*dest; int32_t i;
- if ( scriptPubKey.size() == 35 )
- {
- dest = (uint8_t *)pk.begin();
- for (i=0; i<33; i++)
- dest[i] = scriptPubKey[i+1];
- } else fprintf(stderr,"DiceFundingPk invalid size.%d\n",(int32_t)scriptPubKey.size());
- return(pk);
-}
-
-uint256 DiceHashEntropy(uint256 &entropy,uint256 _txidpriv,int32_t vout,int32_t usevout)
-{
- int32_t i; uint8_t _entropy[32],_hentropy[32]; bits256 tmp256,txidpub,txidpriv,mypriv,mypub,ssecret,ssecret2; uint256 hentropy;
- memset(&hentropy,0,32);
- endiancpy(txidpriv.bytes,(uint8_t *)&_txidpriv,32);
- if ( usevout != 0 )
- {
- txidpriv.bytes[1] ^= (vout & 0xff);
- txidpriv.bytes[2] ^= ((vout>>8) & 0xff);
- }
- txidpriv.bytes[0] &= 0xf8, txidpriv.bytes[31] &= 0x7f, txidpriv.bytes[31] |= 0x40;
- txidpub = curve25519(txidpriv,curve25519_basepoint9());
-
- Myprivkey(tmp256.bytes);
- vcalc_sha256(0,mypriv.bytes,tmp256.bytes,32);
- mypriv.bytes[0] &= 0xf8, mypriv.bytes[31] &= 0x7f, mypriv.bytes[31] |= 0x40;
- mypub = curve25519(mypriv,curve25519_basepoint9());
-
- ssecret = curve25519(mypriv,txidpub);
- ssecret2 = curve25519(txidpriv,mypub);
- if ( memcmp(ssecret.bytes,ssecret2.bytes,32) == 0 )
- {
- vcalc_sha256(0,(uint8_t *)&_entropy,ssecret.bytes,32);
- vcalc_sha256(0,(uint8_t *)&_hentropy,_entropy,32);
- endiancpy((uint8_t *)&entropy,_entropy,32);
- endiancpy((uint8_t *)&hentropy,_hentropy,32);
- }
- else
- {
- for (i=0; i<32; i++)
- fprintf(stderr,"%02x",ssecret.bytes[i]);
- fprintf(stderr," ssecret\n");
- for (i=0; i<32; i++)
- fprintf(stderr,"%02x",ssecret2.bytes[i]);
- fprintf(stderr," ssecret2 dont match\n");
- }
- memset(tmp256.bytes,0,32);
- //char str[65],str2[65];
- //fprintf(stderr,"generated house hentropy.%s <- entropy.%s\n",uint256_str(str,hentropy),uint256_str(str2,entropy));
- return(hentropy);
-}
-
-int32_t dice_5nibbles(uint8_t *fivevals)
-{
- return(((int32_t)fivevals[0]<<16) + ((int32_t)fivevals[1]<<12) + ((int32_t)fivevals[2]<<8) + ((int32_t)fivevals[3]<<4) + ((int32_t)fivevals[4]));
-}
-
-uint64_t DiceCalc(int64_t bet,int64_t vout2,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks,uint256 houseentropy,uint256 bettorentropy)
-{
- uint8_t buf[64],_house[32],_bettor[32],_hash[32],hash[32],hash16[64]; uint64_t odds,winnings; arith_uint256 house,bettor; char str[65],str2[65]; int32_t i,modval;
- if ( vout2 <= 10000 )
- {
- fprintf(stderr,"unexpected vout2.%llu\n",(long long)vout2);
- return(0);
- }
- else odds = (vout2 - 10000);
- if ( bet < minbet || bet > maxbet )
- {
- CCerror = strprintf("bet size violation %.8f",(double)bet/COIN);
- fprintf(stderr,"%s\n", CCerror.c_str() );
- return(0);
- }
- if ( odds > maxodds )
- {
- CCerror = strprintf("invalid odds %d, must be <= %d",odds, maxodds);
- fprintf(stderr,"%s\n", CCerror.c_str() );
- return(0);
- }
-
- endiancpy(buf,(uint8_t *)&houseentropy,32);
- endiancpy(&buf[32],(uint8_t *)&bettorentropy,32);
- vcalc_sha256(0,(uint8_t *)&_house,buf,64);
- endiancpy((uint8_t *)&house,_house,32);
-
- endiancpy(buf,(uint8_t *)&bettorentropy,32);
- endiancpy(&buf[32],(uint8_t *)&houseentropy,32);
- vcalc_sha256(0,(uint8_t *)&_bettor,buf,64);
- endiancpy((uint8_t *)&bettor,_bettor,32);
- winnings = 0;
- //fprintf(stderr,"calc house entropy %s vs bettor %s\n",uint256_str(str,*(uint256 *)&house),uint256_str(str2,*(uint256 *)&bettor));
- if ( odds > 1 )
- {
- if ( 0 )
- { // old way
- bettor = (bettor / arith_uint256(odds));
- if ( bettor >= house )
- winnings = bet * (odds+1);
- return(winnings);
- }
- if ( odds > 9999 ) // shouldnt happen
- return(0);
- endiancpy(buf,(uint8_t *)&house,32);
- endiancpy(&buf[32],(uint8_t *)&bettor,32);
- vcalc_sha256(0,(uint8_t *)&_hash,buf,64);
- endiancpy(hash,_hash,32);
- for (i=0; i<32; i++)
- {
- hash16[i<<1] = ((hash[i] >> 4) & 0x0f);
- hash16[(i<<1) + 1] = (hash[i] & 0x0f);
- }
- modval = 0;
- for (i=0; i<12; i++)
- {
- modval = dice_5nibbles(&hash16[i*5]);
- if ( modval < 1000000 )
- {
- modval %= 10000;
- break;
- }
- }
- //fprintf(stderr,"modval %d vs %d\n",modval,(int32_t)(10000/(odds+1)));
- if ( modval < 10000/(odds+1) )
- winnings = bet * (odds+1);
- }
- else if ( bettor >= house )
- winnings = bet * (odds+1);
- return(winnings);
-}
-
-CScript EncodeDiceFundingOpRet(uint8_t funcid,uint64_t sbits,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks)
-{
- CScript opret; uint8_t evalcode = EVAL_DICE;
- opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'F' << sbits << minbet << maxbet << maxodds << timeoutblocks);
- return(opret);
-}
-
-uint8_t DecodeDiceFundingOpRet(const CScript &scriptPubKey,uint64_t &sbits,int64_t &minbet,int64_t &maxbet,int64_t &maxodds,int64_t &timeoutblocks)
-{
- std::vector 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 >> sbits; ss >> minbet; ss >> maxbet; ss >> maxodds; ss >> timeoutblocks) != 0 )
- {
- if ( e == EVAL_DICE && f == 'F' )
- return(f);
- }
- return(0);
-}
-
-CScript EncodeDiceOpRet(uint8_t funcid,uint64_t sbits,uint256 fundingtxid,uint256 hash,uint256 proof)
-{
- CScript opret; uint8_t evalcode = EVAL_DICE;
- opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << sbits << fundingtxid << hash << proof);
- return(opret);
-}
-
-uint8_t DecodeDiceOpRet(uint256 txid,const CScript &scriptPubKey,uint64_t &sbits,uint256 &fundingtxid,uint256 &hash,uint256 &proof)
-{
- std::vector vopret; uint8_t *script,e,f,funcid; int64_t minbet,maxbet,maxodds,timeoutblocks;
- //script = (uint8_t *)scriptPubKey.data();
- //fprintf(stderr,"decode %02x %02x %02x\n",script[0],script[1],script[2]);
- GetOpReturnData(scriptPubKey,vopret);
- if ( vopret.size() > 2 )//&& script[0] == 0x6a )
- {
- script = (uint8_t *)vopret.data();
- if ( script[0] == EVAL_DICE )
- {
- if ( script[1] == 'F' )
- {
- if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> minbet; ss >> maxbet; ss >> maxodds; ss >> timeoutblocks) != 0 )
- {
- memset(&hash,0,32);
- fundingtxid = txid;
- return('F');
- } else fprintf(stderr,"unmarshal error for F\n");
- }
- else if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> fundingtxid; ss >> hash; ss >> proof) != 0 )
- {
- if ( e == EVAL_DICE && (f == 'R' || f == 'B' || f == 'W' || f == 'L' || f == 'T' || f == 'E') )
- return(f);
- else fprintf(stderr,"mismatched e.%02x f.(%c)\n",e,f);
- }
- } else fprintf(stderr,"script[0] %02x != EVAL_DICE\n",script[0]);
- } else fprintf(stderr,"not enough opret.[%d]\n",(int32_t)vopret.size());
- return(0);
-}
-
-uint256 DiceGetEntropy(CTransaction tx,uint8_t reffuncid)
-{
- uint256 hash,fundingtxid,proof; uint64_t sbits; int32_t numvouts;
- if ( (numvouts= tx.vout.size()) > 0 && DecodeDiceOpRet(tx.GetHash(),tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid,hash,proof) == reffuncid )
- return(hash);
- else return(zeroid);
-}
-
-uint64_t IsDicevout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v,uint64_t refsbits,uint256 reffundingtxid)
-{
- char destaddr[64]; uint8_t funcid; int32_t numvouts; uint64_t sbits; uint256 fundingtxid,hash,proof;
- if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 )
- {
- if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 && (numvouts= tx.vout.size()) > 0 )
- {
- if ( (funcid= DecodeDiceOpRet(tx.GetHash(),tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 && sbits == refsbits && ((funcid == 'F' && tx.GetHash() == reffundingtxid) || fundingtxid == reffundingtxid) )
- return(tx.vout[v].nValue);
- }
- }
- return(0);
-}
-
-int64_t DiceAmounts(uint64_t &inputs,uint64_t &outputs,struct CCcontract_info *cp,Eval *eval,const CTransaction &tx,uint64_t refsbits,uint256 reffundingtxid)
-{
- CTransaction vinTx; uint256 hashBlock; int32_t i,numvins,numvouts; uint64_t assetoshis;
- numvins = tx.vin.size();
- numvouts = tx.vout.size();
- inputs = outputs = 0;
- for (i=0; iismyvin)(tx.vin[i].scriptSig) != 0 )
- {
- if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 )
- return eval->Invalid("always should find vin, but didnt");
- else
- {
- if ( (assetoshis= IsDicevout(cp,vinTx,(int32_t)tx.vin[i].prevout.n,refsbits,reffundingtxid)) != 0 )
- inputs += assetoshis;
- }
- }
- }
- for (i=0; i 1 && DiceIsmine(vinTx.vout[1].scriptPubKey) != 0 && vinTx.vout[0].scriptPubKey.IsPayToCryptoCondition() != 0 )
- {
- if ( ((funcid= DecodeDiceOpRet(txid,vinTx.vout[vinTx.vout.size()-1].scriptPubKey,vinsbits,vinfundingtxid,hentropy,vinproof)) == 'E' || funcid == 'W' || funcid == 'L') && sbits == vinsbits && fundingtxid == vinfundingtxid )
- {
- hentropy2 = DiceHashEntropy(entropy,vinTx.vin[0].prevout.hash,vinTx.vin[0].prevout.n,0);
- entropyvout = vinTx.vin[0].prevout.n;
- //fprintf(stderr,"bettxid %s -> vin0 %s/v%d -> %s\n",txid.GetHex().c_str(),vinTx.vin[0].prevout.hash.GetHex().c_str(),entropyvout,entropy.GetHex().c_str());
- if ( hentropy != hentropy2 )
- {
- hentropy2 = DiceHashEntropy(entropy,vinTx.vin[0].prevout.hash,vinTx.vin[0].prevout.n,1);
- //fprintf(stderr,"alt bettxid %s -> vin0 %s/v%d -> %s\n",txid.GetHex().c_str(),vinTx.vin[0].prevout.hash.GetHex().c_str(),entropyvout,entropy.GetHex().c_str());
- }
- if ( hentropy == hentropy2 )
- {
- winnings = DiceCalc(tx.vout[1].nValue,tx.vout[2].nValue,minbet,maxbet,maxodds,timeoutblocks,entropy,bettorentropy);
- //char str[65]; fprintf(stderr,"%s winnings %.8f bet %.8f at odds %d:1\n",uint256_str(str,tx.GetHash()),(double)winnings/COIN,(double)tx.vout[1].nValue/COIN,(int32_t)(tx.vout[2].nValue-10000));
- //fprintf(stderr,"I am house entropy %.8f entropy.(%s) vs %s -> winnings %.8f\n",(double)vinTx.vout[0].nValue/COIN,uint256_str(str,entropy),uint256_str(str2,hash),(double)winnings/COIN);
- if ( winnings == 0 )
- {
- // queue 'L' losing tx
- return(-1);
- }
- else
- {
- // queue 'W' winning tx
- return(1);
- }
- }
- else
- {
- fprintf(stderr,"both hentropy != hentropy2\n");
- }
- } else fprintf(stderr,"funcid.%c sbits %llx vs %llx cmp.%d\n",funcid,(long long)sbits,(long long)vinsbits,fundingtxid == vinfundingtxid);
- } //else fprintf(stderr,"notmine.%d or not CC.%d\n",DiceIsmine(vinTx.vout[1].scriptPubKey) != 0,vinTx.vout[0].scriptPubKey.IsPayToCryptoCondition() != 0);
- return(0);
-}
-
-bool DiceVerifyTimeout(CTransaction &betTx,int32_t timeoutblocks)
-{
- int32_t numblocks;
- if ( CCduration(numblocks,betTx.GetHash()) <= 0 )
- return(false);
- return(numblocks >= timeoutblocks);
-}
-
-bool DiceValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx, uint32_t nIn)
-{
- uint256 txid,fundingtxid,vinfundingtxid,vinhentropy,vinproof,hashBlock,hash,proof,entropy; int64_t minbet,maxbet,maxodds,timeoutblocks,odds,winnings; uint64_t vinsbits,refsbits=0,sbits,amount,inputs,outputs,txfee=10000; int32_t numvins,entropyvout,numvouts,preventCCvins,preventCCvouts,i,iswin; uint8_t funcid; CScript fundingPubKey; CTransaction fundingTx,vinTx,vinofvinTx; char CCaddr[64];
- numvins = tx.vin.size();
- numvouts = tx.vout.size();
- preventCCvins = preventCCvouts = -1;
- if ( numvouts < 1 )
- return eval->Invalid("no vouts");
- else
- {
- txid = tx.GetHash();
- if ( (funcid= DecodeDiceOpRet(txid,tx.vout[numvouts-1].scriptPubKey,refsbits,fundingtxid,hash,proof)) != 0 )
- {
- if ( eval->GetTxUnconfirmed(fundingtxid,fundingTx,hashBlock) == 0 )
- return eval->Invalid("cant find fundingtxid");
- else if ( fundingTx.vout.size() > 0 && DecodeDiceFundingOpRet(fundingTx.vout[fundingTx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) != 'F' )
- return eval->Invalid("fundingTx not valid");
- if ( maxodds > 9999 )
- return eval->Invalid("maxodds too big");
- fundingPubKey = fundingTx.vout[1].scriptPubKey;
- if ( sbits != refsbits )
- {
- fprintf(stderr,"VALIDATION ERROR: sbits %llx != refsbits %llx\n",(long long)sbits,(long long)refsbits);
- //return eval->Invalid("mismatched diceplan");
- }
- switch ( funcid )
- {
- case 'F':
- //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 minbet maxbet maxodds timeoutblocks
- return eval->Invalid("unexpected DiceValidate for createfunding");
- break;
- case 'E': // check sig of vin to match fundingtxid in the 'B' tx
- //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
- return eval->Invalid("unexpected DiceValidate for addfunding entropy");
- break;
- case 'B':
- //vin.0: entropy txid from house
- //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
- preventCCvouts = 2;
- preventCCvins = 1;
- if ( IsCCInput(tx.vin[0].scriptSig) == 0 )
- return eval->Invalid("vin.0 is normal for bet");
- else if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 )
- return eval->Invalid("vout.0 is normal for bet");
- else if ( tx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 )
- return eval->Invalid("vout.1 is normal for bet");
- else if ( eval->GetTxUnconfirmed(tx.vin[0].prevout.hash,vinTx,hashBlock) == 0 )
- return eval->Invalid("always should find vin.0, but didnt for bet");
- else if ( vinTx.vout[1].scriptPubKey != fundingPubKey )
- return eval->Invalid("entropy tx not fundingPubKey for bet");
- else if ( ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,(int64_t)vinTx.vout[tx.vin[0].prevout.n].nValue) == 0 )
- {
- fprintf(stderr,"%s prevout.%d %.8f\n",tx.vin[0].prevout.hash.GetHex().c_str(),(int32_t)tx.vin[0].prevout.n,(double)vinTx.vout[tx.vin[0].prevout.n].nValue/COIN);
- return eval->Invalid("vout[0] != entropy nValue for bet");
- }
- else if ( ConstrainVout(tx.vout[1],1,cp->unspendableCCaddr,0) == 0 )
- return eval->Invalid("vout[1] constrain violation for bet");
- else if ( tx.vout[2].nValue > txfee+maxodds || tx.vout[2].nValue <= txfee )
- return eval->Invalid("vout[2] nValue violation for bet");
- else if ( eval->GetTxUnconfirmed(vinTx.vin[0].prevout.hash,vinofvinTx,hashBlock) == 0 || vinofvinTx.vout.size() < 1 )
- return eval->Invalid("always should find vinofvin.0, but didnt for bet");
- else if ( vinTx.vin[0].prevout.hash != fundingtxid )
- {
- if ( (int32_t)vinTx.vin[0].prevout.n < 0 || vinofvinTx.vout[vinTx.vin[0].prevout.n].scriptPubKey != fundingPubKey )
- {
- uint8_t *ptr0,*ptr1; int32_t i; char str[65],addr0[64],addr1[64];
- Getscriptaddress(addr0,vinofvinTx.vout[vinTx.vin[0].prevout.n].scriptPubKey);
- Getscriptaddress(addr1,fundingPubKey);
- if ( strcmp(addr0,addr1) != 0 )
- {
- fprintf(stderr,"%s != %s betTx.%s\n",addr0,addr1,uint256_str(str,txid));
- fprintf(stderr,"entropyTx.%s v%d\n",uint256_str(str,tx.vin[0].prevout.hash),(int32_t)tx.vin[0].prevout.n);
- fprintf(stderr,"entropyTx vin0 %s v%d\n",uint256_str(str,vinTx.vin[0].prevout.hash),(int32_t)vinTx.vin[0].prevout.n);
- ptr0 = (uint8_t *)&vinofvinTx.vout[vinTx.vin[0].prevout.n].scriptPubKey[0];
- ptr1 = (uint8_t *)&fundingPubKey[0];
- for (i=0; iInvalid("vin1 of entropy tx not fundingPubKey for bet");
- }
- }
- }
- if ( (iswin= DiceIsWinner(entropy,entropyvout,txid,tx,vinTx,hash,sbits,minbet,maxbet,maxodds,timeoutblocks,fundingtxid)) != 0 )
- {
- // will only happen for fundingPubKey
- if ( HUSH_INSYNC != 0 && HUSH_DEALERNODE != 0 )
- DiceQueue(iswin,sbits,fundingtxid,txid,tx,entropyvout);
- }
- else
- {
- //fprintf(stderr,"why does node1 get VALIDATION ERROR: invalid dicebet bettxid %s\n",txid.GetHex().c_str());
- //return eval->Invalid("invalid dicebet bettxid");
- }
- break;
- // make sure all funding txid are from matching sbits and fundingtxid!!
- case 'L':
- case 'W':
- case 'T':
- //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.1: tag to owner address for entropy funds
- preventCCvouts = 1;
- DiceAmounts(inputs,outputs,cp,eval,tx,sbits,fundingtxid);
- if ( IsCCInput(tx.vin[1].scriptSig) == 0 || IsCCInput(tx.vin[2].scriptSig) == 0 )
- return eval->Invalid("vin0 or vin1 normal vin for bet");
- else if ( tx.vin[1].prevout.hash != tx.vin[2].prevout.hash )
- return eval->Invalid("vin0 != vin1 prevout.hash for bet");
- else if ( eval->GetTxUnconfirmed(tx.vin[1].prevout.hash,vinTx,hashBlock) == 0 )
- return eval->Invalid("always should find vin.0, but didnt for wlt");
- else if ( vinTx.vout.size() < 3 || DecodeDiceOpRet(tx.vin[1].prevout.hash,vinTx.vout[vinTx.vout.size()-1].scriptPubKey,vinsbits,vinfundingtxid,vinhentropy,vinproof) != 'B' )
- return eval->Invalid("not betTx for vin0/1 for wlt");
- else if ( sbits != vinsbits || fundingtxid != vinfundingtxid )
- return eval->Invalid("sbits or fundingtxid mismatch for wlt");
- else if ( fundingPubKey != tx.vout[1].scriptPubKey )
- return eval->Invalid("tx.vout[1] != fundingPubKey for wlt");
- if ( funcid == 'L' )
- {
- //vout.0: funding CC to entropy owner
- //vout.n-1: opreturn 'L' sbits fundingtxid hentropy proof
- if ( ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,inputs) == 0 )
- return eval->Invalid("vout[0] != inputs-txfee for loss");
- else if ( tx.vout[2].scriptPubKey != fundingPubKey )
- {
- if ( tx.vout[2].scriptPubKey.size() == 0 || tx.vout[2].scriptPubKey[0] != 0x6a )
- return eval->Invalid("vout[2] not send to fundingPubKey for loss");
- }
- iswin = -1;
- }
- else
- {
- //vout.0: funding CC change to entropy owner
- //vout.2: normal output to bettor's address
- //vout.n-1: opreturn 'W' sbits fundingtxid hentropy proof
- odds = vinTx.vout[2].nValue - txfee;
- if ( ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,0) == 0 )
- return eval->Invalid("vout[0] != inputs-txfee for win/timeout");
- else if ( tx.vout[2].scriptPubKey != vinTx.vout[2].scriptPubKey )
- return eval->Invalid("vout[2] scriptPubKey mismatch for win/timeout");
- else if ( tx.vout[2].nValue != (odds+1)*vinTx.vout[1].nValue )
- return eval->Invalid("vout[2] payut mismatch for win/timeout");
- else if ( inputs != (outputs + tx.vout[2].nValue) && inputs != (outputs + tx.vout[2].nValue+txfee) )
- {
- fprintf(stderr,"inputs %.8f != outputs %.8f (%.8f %.8f %.8f %.8f)\n",(double)inputs/COIN,(double)outputs/COIN,(double)tx.vout[0].nValue/COIN,(double)tx.vout[1].nValue/COIN,(double)tx.vout[2].nValue/COIN,(double)tx.vout[3].nValue/COIN);
- return eval->Invalid("CC funds mismatch for win/timeout");
- }
- else if ( tx.vout[3].scriptPubKey != fundingPubKey )
- {
- if ( tx.vout[3].scriptPubKey.size() == 0 || tx.vout[3].scriptPubKey[0] != 0x6a )
- return eval->Invalid("vout[3] not send to fundingPubKey for win/timeout");
- }
- iswin = (funcid == 'W');
- }
- if ( iswin != 0 )
- {
- //char str[65],str2[65];
- entropy = DiceGetEntropy(vinTx,'B');
- vcalc_sha256(0,(uint8_t *)&hash,(uint8_t *)&proof,32);
- //fprintf(stderr,"calculated house hentropy.%s\n",uint256_str(str,hash));
- //fprintf(stderr,"verify house entropy %s vs bettor %s\n",uint256_str(str,proof),uint256_str(str2,entropy));
- winnings = DiceCalc(vinTx.vout[1].nValue,vinTx.vout[2].nValue,minbet,maxbet,maxodds,timeoutblocks,proof,entropy);
- if ( (winnings == 0 && iswin > 0) || (winnings > 0 && iswin < 0) )
- return eval->Invalid("DiceCalc mismatch for win/loss");
- }
- else if ( DiceVerifyTimeout(vinTx,timeoutblocks) == 0 )
- return eval->Invalid("invalid timeout claim for timeout");
- break;
- case 'R':
- if ( eval->GetTxUnconfirmed(tx.vin[0].prevout.hash,vinTx,hashBlock) == 0 )
- return eval->Invalid("always should find vin.0, but didnt for refund");
- else if ( vinTx.vout[tx.vin[0].prevout.n].scriptPubKey != fundingPubKey )
- {
- char fundingaddr[64],cmpaddr[64];
- Getscriptaddress(fundingaddr,fundingPubKey);
- Getscriptaddress(cmpaddr,vinTx.vout[tx.vin[0].prevout.n].scriptPubKey);
- if ( strcmp(cmpaddr,fundingaddr) != 0 )
- {
- fprintf(stderr,"cmpaddr.%s != fundingaddr.%s\n",cmpaddr,fundingaddr);
- return eval->Invalid("vin.0 not from fundingPubKey for refund");
- }
- }
- if ( (rand() % 1000) == 0 )
- fprintf(stderr,"add more validation for refunds\n");
- break;
- default:
- fprintf(stderr,"illegal dice funcid.(%c)\n",funcid);
- return eval->Invalid("unexpected dice funcid");
- break;
- }
- } else return eval->Invalid("unexpected dice missing funcid");
- return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts));
- }
- return(true);
-}
-
-uint64_t AddDiceInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint64_t total,int32_t maxinputs,uint64_t refsbits,uint256 reffundingtxid)
-{
- char coinaddr[64],str[65]; uint64_t threshold,sbits,nValue,totalinputs = 0; uint256 txid,hash,proof,hashBlock,fundingtxid; CTransaction tx; int32_t j,vout,n = 0; uint8_t funcid;
- std::vector > unspentOutputs;
- GetCCaddress(cp,coinaddr,pk);
- SetCCunspents(unspentOutputs,coinaddr,true);
- if ( maxinputs > CC_MAXVINS )
- maxinputs = CC_MAXVINS;
- if ( maxinputs > 0 )
- threshold = total / maxinputs;
- else threshold = total;
- for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
- {
- txid = it->first.txhash;
- vout = (int32_t)it->first.index;
- if ( vout != 0 || it->second.satoshis < threshold )
- continue;
- //fprintf(stderr,"(%s) %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN);
- for (j=0; j 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 )
- {
- if ( (funcid= DecodeDiceOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 )
- {
- char str[65],sstr[16];
- unstringbits(sstr,sbits);
- if ( sbits == refsbits && (funcid == 'F' && reffundingtxid == txid) || reffundingtxid == fundingtxid )
- {
- if ( funcid == 'R' || funcid == 'F' || funcid == 'E' || funcid == 'W' || funcid == 'L' || funcid == 'T' )
- {
- if ( total != 0 && maxinputs != 0 )
- {
- if ( funcid == 'R' )
- fprintf(stderr,">>>>>>>>>>>> use (%c) %.8f %s %s/v%d\n",funcid,(double)tx.vout[0].nValue/COIN,sstr,uint256_str(str,txid),vout);
- mtx.vin.push_back(CTxIn(txid,vout,CScript()));
- }
- totalinputs += it->second.satoshis;
- n++;
- if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) )
- break;
- }
- }
- } else fprintf(stderr,"null funcid\n");
- }
- }
- return(totalinputs);
-}
-
-int64_t DicePlanFunds(uint64_t &entropyval,uint256 &entropytxid,uint64_t refsbits,struct CCcontract_info *cp,CPubKey dicepk,uint256 reffundingtxid, int32_t &entropytxs,bool random)
-{
- char coinaddr[64],str[65]; uint64_t sbits; int64_t nValue,sum,totalinputs = 0; uint256 hash,txid,proof,hashBlock,fundingtxid; CScript fundingPubKey; CTransaction tx,vinTx; int32_t vout,first=0,n=0,i=0,pendingbets=0; uint8_t funcid;
- std::vector > unspentOutputs;
- entropyval = 0;
- entropytxid = zeroid;
- if ( myGetTransaction(reffundingtxid,tx,hashBlock) != 0 && tx.vout.size() > 1 && ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,0) != 0 )
- {
- fundingPubKey = tx.vout[1].scriptPubKey;
- } else return(0);
- GetCCaddress(cp,coinaddr,dicepk);
- SetCCunspents(unspentOutputs,coinaddr,true);
- entropyval = 0;
- int loops = 0;
- int numtxs = unspentOutputs.size()/2;
- int startfrom = rand() % (numtxs+1);
- for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
- {
- txid = it->first.txhash;
- vout = (int32_t)it->first.index;
- if ( vout != 0 )
- continue;
- sum += it->second.satoshis;
- loops++;
- if (random) {
- if ( loops < startfrom )
- continue;
- if ( (rand() % 100) < 90 )
- continue;
- }
- if ( myGetTransaction(txid,tx,hashBlock) != 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 )
- {
- if ( (funcid= DecodeDiceOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 && sbits == refsbits )
- {
- if ( funcid == 'B' )
- {
- pendingbets++;
- fprintf(stderr,"%d: %s/v%d (%c %.8f) %.8f %.8f\n",n,uint256_str(str,txid),vout,funcid,(double)it->second.satoshis/COIN,(double)totalinputs/COIN,(double)sum/COIN);
- }
- if ( (funcid == 'F' && reffundingtxid == txid) || reffundingtxid == fundingtxid )
- {
- //fprintf(stderr,"%d: %s/v%d (%c %.8f) %.8f %.8f\n",n,uint256_str(str,txid),vout,funcid,(double)it->second.satoshis/COIN,(double)totalinputs/COIN,(double)sum/COIN);
- if ( (nValue= IsDicevout(cp,tx,vout,refsbits,reffundingtxid)) >= 10000 && (funcid == 'R' || funcid == 'F' || funcid == 'E' || funcid == 'W' || funcid == 'L' || funcid == 'T') )
- {
- if ( funcid == 'L' || funcid == 'W' || funcid == 'E' )
- n++;
- totalinputs += nValue;
- if ( first == 0 && (funcid == 'E' || funcid == 'W' || funcid == 'L') )
- {
- //fprintf(stderr,"check first\n");
- if ( tx.vout.size() > 1 && fundingPubKey == tx.vout[1].scriptPubKey )
- {
- if ( myGetTransaction(tx.vin[0].prevout.hash,vinTx,hashBlock) == 0 || (int32_t)tx.vin[0].prevout.n < 0 )
- {
- fprintf(stderr,"cant find entropy vin0 %s or vin0prev %d vouts[%d], iscoinbase.%d\n",uint256_str(str,tx.vin[0].prevout.hash),(int32_t)tx.vin[0].prevout.n,(int32_t)vinTx.vout.size(),(int32_t)vinTx.vin.size());
- continue;
- }
- if ( (int32_t)vinTx.vin[0].prevout.n < 0 || vinTx.vout.size() < 2 )
- {
- fprintf(stderr,"skip coinbase or strange entropy tx\n");
- continue;
- }
- //if ( fundingtxid != tx.vin[0].prevout.hash && vinTx.vout[tx.vin[0].prevout.n].scriptPubKey != fundingPubKey )
- if ( fundingtxid != tx.vin[0].prevout.hash && vinTx.vout[1].scriptPubKey != fundingPubKey )
- {
- uint8_t *ptr0,*ptr1; int32_t i; char str[65],addr0[64],addr1[64];
- Getscriptaddress(addr0,vinTx.vout[1].scriptPubKey);
- Getscriptaddress(addr1,fundingPubKey);
- if ( strcmp(addr0,addr1) != 0 )
- {
- ptr0 = (uint8_t *)&vinTx.vout[1].scriptPubKey[0];
- ptr1 = (uint8_t *)&fundingPubKey[0];
- for (i=0; i txids;
- GetCCaddress(cp,CCaddr,dicepk);
- SetCCtxids(txids,cp->normaladdr,false,cp->evalcode,zeroid,'F');
- if ( fundingtxid != zeroid ) // avoid scan unless creating new funding plan
- {
- //fprintf(stderr,"check fundingtxid\n");
- if ( myGetTransaction(fundingtxid,tx,hashBlock) != 0 && tx.vout.size() > 1 && ConstrainVout(tx.vout[0],1,CCaddr,0) != 0 )
- {
- if ( DecodeDiceFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) == 'F' && sbits == refsbits )
- {
- fundingPubKey = tx.vout[1].scriptPubKey;
- return(true);
- } else fprintf(stderr,"error decoding opret or sbits mismatch %llx vs %llx\n",(long long)sbits,(long long)refsbits);
- } else fprintf(stderr,"couldnt get funding tx\n");
- return(false);
- }
- for (std::vector::const_iterator it=txids.begin(); it!=txids.end(); it++)
- {
- txid = *it;
- if ( fundingtxid != zeroid && txid != fundingtxid )
- continue;
- if ( myGetTransaction(txid,tx,hashBlock) != 0 && tx.vout.size() > 0 && ConstrainVout(tx.vout[0],1,CCaddr,0) != 0 )
- {
- if ( DecodeDiceFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) == 'F' )
- {
- if ( sbits == refsbits )
- {
- fundingPubKey = tx.vout[1].scriptPubKey;
- fundingtxid = txid;
- return(true);
- }
- }
- }
- }
- return(false);
-}
-
-struct CCcontract_info *Diceinit(CScript &fundingPubKey,uint256 reffundingtxid,struct CCcontract_info *C,char *planstr,uint64_t &txfee,CPubKey &mypk,CPubKey &dicepk,uint64_t &sbits,int64_t &minbet,int64_t &maxbet,int64_t &maxodds,int64_t &timeoutblocks)
-{
- struct CCcontract_info *cp; int32_t cmpflag;
- cp = CCinit(C,EVAL_DICE);
- if ( txfee == 0 )
- txfee = 10000;
- mypk = pubkey2pk(Mypubkey());
- dicepk = GetUnspendable(cp,0);
- sbits = stringbits(planstr);
- if ( reffundingtxid == zeroid )
- cmpflag = 0;
- else cmpflag = 1;
- if ( DicePlanExists(fundingPubKey,reffundingtxid,cp,sbits,dicepk,minbet,maxbet,maxodds,timeoutblocks) != cmpflag )
- {
- fprintf(stderr,"Dice plan (%s) exists.%d vs cmpflag.%d\n",planstr,!cmpflag,cmpflag);
- return(0);
- }
- return(cp);
-}
-
-UniValue DiceInfo(uint256 diceid)
-{
- UniValue result(UniValue::VOBJ); CPubKey dicepk; uint256 hashBlock,entropytxid; CTransaction vintx; int64_t minbet,maxbet,maxodds,timeoutblocks; uint64_t sbits,funding,entropyval; char str[67],numstr[65]; struct CCcontract_info *cp,C;
- if ( myGetTransaction(diceid,vintx,hashBlock) == 0 )
- {
- fprintf(stderr,"cant find fundingtxid\n");
- ERR_RESULT("cant find fundingtxid");
- return(result);
- }
- if ( vintx.vout.size() > 0 && DecodeDiceFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) == 0 )
- {
- fprintf(stderr,"fundingtxid isnt dice creation txid\n");
- ERR_RESULT("fundingtxid isnt dice creation txid");
- return(result);
- }
- result.push_back(Pair("result","success"));
- result.push_back(Pair("fundingtxid",uint256_str(str,diceid)));
- unstringbits(str,sbits);
- result.push_back(Pair("name",str));
- result.push_back(Pair("sbits",sbits));
- 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));
- result.push_back(Pair("timeoutblocks",timeoutblocks));
- cp = CCinit(&C,EVAL_DICE);
- dicepk = GetUnspendable(cp,0);
- int32_t entropytxs;
- funding = DicePlanFunds(entropyval,entropytxid,sbits,cp,dicepk,diceid,entropytxs,false);
- sprintf(numstr,"%.8f",(double)funding/COIN);
- result.push_back(Pair("funding",numstr));
- result.push_back(Pair("entropytxs",entropytxs));
- return(result);
-}
-
-UniValue DiceList()
-{
- UniValue result(UniValue::VARR); std::vector txids; struct CCcontract_info *cp,C; uint256 txid,hashBlock; CTransaction vintx; uint64_t sbits; int64_t minbet,maxbet,maxodds,timeoutblocks; char str[65];
- cp = CCinit(&C,EVAL_DICE);
- SetCCtxids(txids,cp->normaladdr,false,cp->evalcode,zeroid,'F');
- for (std::vector::const_iterator it=txids.begin(); it!=txids.end(); it++)
- {
- txid = *it;
- if ( myGetTransaction(txid,vintx,hashBlock) != 0 )
- {
- if ( vintx.vout.size() > 0 && DecodeDiceFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) != 0 )
- {
- result.push_back(uint256_str(str,txid));
- }
- }
- }
- return(result);
-}
-
-std::string DiceCreateFunding(uint64_t txfee,char *planstr,int64_t funds,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks)
-{
- CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
- uint256 zero; CScript fundingPubKey; CPubKey mypk,dicepk; 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 = "dice plan needs at least 100 coins";
- fprintf(stderr,"%s\n", CCerror.c_str() );
- return("");
- }
- memset(&zero,0,sizeof(zero));
- if ( (cp= Diceinit(fundingPubKey,zero,&C,planstr,txfee,mypk,dicepk,sbits,a,b,c,d)) == 0 )
- {
- CCerror = "Diceinit error in create funding, is your transaction confirmed?";
- 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,dicepk));
- mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
- mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(dicepk)) << OP_CHECKSIG));
- return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceFundingOpRet('F',sbits,minbet,maxbet,maxodds,timeoutblocks)));
- }
- CCerror = "cant find enough inputs";
- fprintf(stderr,"%s\n", CCerror.c_str() );
- return("");
-}
-
-std::string DiceAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t amount)
-{
- CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
- CScript fundingPubKey,scriptPubKey; uint256 entropy,hentropy; CPubKey mypk,dicepk; uint64_t sbits; struct CCcontract_info *cp,C; int64_t minbet,maxbet,maxodds,timeoutblocks;
- if ( amount < 0 )
- {
- CCerror = "amount must be positive";
- fprintf(stderr,"%s\n", CCerror.c_str() );
- return("");
- }
- if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 ) {
- CCerror = "Diceinit error in add funding, is your transaction confirmed?";
- return("");
- }
- scriptPubKey = CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG;
- if ( 0 )
- {
- uint8_t *ptr0,*ptr1; int32_t i;
- for (i=0; i<35; i++)
- fprintf(stderr,"%02x",scriptPubKey[i]);
- fprintf(stderr," script vs ");
- for (i=0; i<35; i++)
- fprintf(stderr,"%02x",fundingPubKey[i]);
- fprintf(stderr," funding\n");
- }
- if ( scriptPubKey == fundingPubKey )
- {
- if ( AddNormalinputs2(mtx,amount+2*txfee,60) > 0 )
- {
- hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash,mtx.vin[0].prevout.n,1);
- mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,dicepk));
- mtx.vout.push_back(CTxOut(txfee,fundingPubKey));
- return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceOpRet('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 DiceBet(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t bet,int32_t odds)
-{
- CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
- CScript fundingPubKey; CPubKey mypk,dicepk; uint64_t sbits,entropyval,entropyval2; int64_t funding,minbet,maxbet,maxodds,timeoutblocks; uint256 entropytxid,entropytxid2,entropy,hentropy; struct CCcontract_info *cp,C;
- if ( bet < 0 )
- {
- CCerror = "bet must be positive";
- return("");
- }
- if ( odds < 2 || odds > 9999 )
- {
- CCerror = "odds must be between 2 and 9999";
- return("");
- }
- if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 ) {
- CCerror = "Diceinit error in bet, is your transaction confirmed?";
- return("");
- }
- if ( bet < minbet || bet > maxbet || odds > maxodds )
- {
- CCerror = strprintf("Dice 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);
- return("");
- }
- int32_t entropytxs=0,emptyvar=0;
- funding = DicePlanFunds(entropyval,entropytxid,sbits,cp,dicepk,fundingtxid,entropytxs,false);
- DicePlanFunds(entropyval2,entropytxid2,sbits,cp,dicepk,fundingtxid,emptyvar,true);
- if ( entropyval2 != 0 && entropytxid2 != zeroid )
- {
- entropyval = entropyval2;
- entropytxid = entropytxid2;
- }
- if ( funding >= 2*bet*odds+txfee && entropyval != 0 )
- {
- if ( entropytxs < 100 ) {
- CCerror = "Your dealer is broke, find a new casino.";
- return("");
- }
- if ( myIsutxo_spentinmempool(ignoretxid,ignorevin,entropytxid,0) != 0 )
- {
- CCerror = "entropy txid is spent";
- return("");
- }
- mtx.vin.push_back(CTxIn(entropytxid,0,CScript()));
- if ( AddNormalinputs(mtx,mypk,bet+2*txfee+odds,60) > 0 )
- {
- hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash,mtx.vin[0].prevout.n,1);
- mtx.vout.push_back(MakeCC1vout(cp->evalcode,entropyval,dicepk));
- mtx.vout.push_back(MakeCC1vout(cp->evalcode,bet,dicepk));
- mtx.vout.push_back(CTxOut(txfee+odds,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
- return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceOpRet('B',sbits,fundingtxid,entropy,zeroid)));
- } else CCerror = "cant find enough normal inputs for %.8f, plan funding %.8f\n";
- }
- if ( entropyval == 0 && funding != 0 )
- CCerror = "cant find dice entropy inputs";
- else
- CCerror = "cant find dice input";
- return("");
-}
-
-std::string DiceBetFinish(uint8_t &funcid,uint256 &entropyused,int32_t &entropyvout,int32_t *resultp,uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 bettxid,int32_t winlosetimeout,uint256 vin0txid,int32_t vin0vout)
-{
- CMutableTransaction savemtx,mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
- CScript scriptPubKey,fundingPubKey; CTransaction oldbetTx,betTx,entropyTx; uint256 hentropyproof,entropytxid,hashBlock,bettorentropy,entropy,hentropy,oldbettxid; CPubKey mypk,dicepk,fundingpk; struct CCcontract_info *cp,C; int64_t inputs=0,CCchange=0,odds,fundsneeded,minbet,maxbet,maxodds,timeoutblocks; int32_t oldentropyvout,retval=0,iswin=0; uint64_t entropyval,sbits;
- entropyused = zeroid;
- *resultp = 0;
- funcid = 0;
- if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 )
- {
- CCerror = "Diceinit error in finish, is your transaction confirmed?";
- fprintf(stderr,"%s\n", CCerror.c_str() );
- return("");
- }
- fundingpk = DiceFundingPk(fundingPubKey);
- scriptPubKey = CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG;
- if ( winlosetimeout != 0 ) // must be dealernode
- {
- if ( scriptPubKey != fundingPubKey )
- {
- //fprintf(stderr,"only dice fund creator can submit winner or loser\n");
- winlosetimeout = 0;
- }
- }
- if ( myGetTransaction(bettxid,betTx,hashBlock) != 0 && myGetTransaction(betTx.vin[0].prevout.hash,entropyTx,hashBlock) != 0 )
- {
- entropytxid = betTx.vin[0].prevout.hash;
- /*if ( dice_betspent((char *)"DiceBetFinish",bettxid) != 0 )
- {
- CCerror = "bettxid already spent";
- fprintf(stderr,"%s\n", CCerror.c_str() );
- return("");
- }*/
- bettorentropy = DiceGetEntropy(betTx,'B');
- if ( winlosetimeout == 0 || (iswin= DiceIsWinner(hentropyproof,entropyvout,bettxid,betTx,entropyTx,bettorentropy,sbits,minbet,maxbet,maxodds,timeoutblocks,fundingtxid)) != 0 )
- {
- if ( vin0txid == zeroid || vin0vout < 0 )
- {
- if ( AddNormalinputs2(mtx,2*txfee,3) == 0 ) // must be a single vin!!
- {
- CCerror = "no txfee inputs for win/lose";
- fprintf(stderr,"%s\n", CCerror.c_str() );
- return("");
- }
- }
- else
- {
- //fprintf(stderr,"use vin0 %s/%d\n",vin0txid.GetHex().c_str(),vin0vout);
- mtx.vin.push_back(CTxIn(vin0txid,vin0vout,CScript()));
- }
- if ( winlosetimeout != 0 ) // dealernode
- {
- entropyused = hentropyproof;
- if ( vin0vout == -2 )
- retval = -1;
- /*if ( iswin == 0 )
- {
- retval = -1;
- fprintf(stderr,"invalid dicebet %s\n",bettxid.GetHex().c_str());
- } else retval = 0;*/
- if ( retval < 0 || (retval= DiceEntropyUsed(oldbetTx,oldbettxid,oldentropyvout,entropyused,bettxid,betTx,entropyvout)) != 0 )
- {
- if ( retval < 0 )
- {
- fprintf(stderr,"orphan that reveals entropy, generate refund tx with proofs\n");
- // make sure we dont refund wrong amounts
- mtx.vin.push_back(CTxIn(bettxid,0,CScript()));
- mtx.vin.push_back(CTxIn(bettxid,1,CScript()));
- funcid = 'R';
- mtx.vout.push_back(MakeCC1vout(cp->evalcode,betTx.vout[0].nValue,dicepk));
- //mtx.vout.push_back(CTxOut(betTx.vout[0].nValue,fundingPubKey));
- mtx.vout.push_back(CTxOut(txfee,fundingPubKey));
- mtx.vout.push_back(CTxOut(betTx.vout[1].nValue,betTx.vout[2].scriptPubKey));
- *resultp = 1;
- return(FinalizeCCTx(0,cp,mtx,fundingpk,txfee,EncodeDiceOpRet(funcid,sbits,fundingtxid,entropyused,oldbettxid))); // need to change opreturn to include oldbetTx to allow validation
- }
- else
- {
- CCerror = "DiceBetFinish: duplicate betTx";
- *resultp = -2; // demote error to warning
- }
- //fprintf(stderr,"%s\n", CCerror.c_str() );
- return("");
- }
- //fprintf(stderr,"set winlosetimeout %d <- %d\n",winlosetimeout,iswin);
- if ( (winlosetimeout= iswin) > 0 )
- funcid = 'W';
- else funcid = 'L';
- }
- if ( iswin == winlosetimeout ) // dealernode and normal node paths should always get here
- {
- //fprintf(stderr,"iswin.%d matches\n",iswin);
- mtx.vin.push_back(CTxIn(bettxid,0,CScript()));
- mtx.vin.push_back(CTxIn(bettxid,1,CScript()));
- if ( iswin == 0 && funcid != 'L' && funcid != 'W' ) // normal node path
- {
- if ( DiceVerifyTimeout(betTx,timeoutblocks) == 0 ) // hasnt timed out yet
- {
- return("");
- }
- else
- {
- funcid = 'T';
- hentropy = hentropyproof = zeroid;
- iswin = 1;
- fprintf(stderr,"set timeout win T\n");
- }
- }
- if ( iswin > 0 && funcid != 0 ) // dealernode 'W' or normal node 'T' path
- {
- odds = (betTx.vout[2].nValue - txfee);
- if ( odds < 1 || odds > maxodds )
- {
- CCerror = strprintf("illegal odds.%d vs maxodds.%d\n",(int32_t)odds,(int32_t)maxodds);
- fprintf(stderr,"%s\n", CCerror.c_str() );
- return("");
- }
- CCchange = betTx.vout[0].nValue + betTx.vout[1].nValue;
- fundsneeded = txfee + (odds+1)*betTx.vout[1].nValue;
- savemtx = mtx;
- if ( CCchange >= fundsneeded )
- CCchange -= fundsneeded;
- else if ( (inputs= AddDiceInputs(cp,mtx,dicepk,fundsneeded,1,sbits,fundingtxid)) >= fundsneeded )
- {
- if ( inputs > fundsneeded )
- CCchange += (inputs - fundsneeded);
- }
- else
- {
- mtx = savemtx;
- if ( (inputs= AddDiceInputs(cp,mtx,dicepk,fundsneeded,60,sbits,fundingtxid)) > 0 )
- {
- if ( inputs > fundsneeded )
- CCchange += (inputs - fundsneeded);
- }
- else
- {
- CCerror = strprintf("not enough inputs for %.8f\n",(double)fundsneeded/COIN);
- fprintf(stderr,"%s\n", CCerror.c_str() );
- return("");
- }
- }
- mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,dicepk));
- mtx.vout.push_back(CTxOut(txfee,fundingPubKey));
- mtx.vout.push_back(CTxOut((odds+1) * betTx.vout[1].nValue,betTx.vout[2].scriptPubKey));
- }
- else // dealernode 'L' path
- {
- funcid = 'L';
- mtx.vout.push_back(MakeCC1vout(cp->evalcode,betTx.vout[0].nValue + betTx.vout[1].nValue,dicepk));
- mtx.vout.push_back(CTxOut(txfee,fundingPubKey));
- }
- //fprintf(stderr,"make tx.%c\n",funcid);
- if ( funcid == 'L' || funcid == 'W' ) // dealernode only
- hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash,mtx.vin[0].prevout.n,1);
- else
- {
- if ( scriptPubKey != betTx.vout[2].scriptPubKey )
- {
- CCerror = strprintf("can only finish your own bettxid\n");
- fprintf(stderr,"%s\n", CCerror.c_str() );
- return("");
- }
- }
- *resultp = 1;
- //char str[65],str2[65];
- //fprintf(stderr,"iswin.%d house entropy %s vs bettor %s\n",iswin,uint256_str(str,hentropyproof),uint256_str(str2,bettorentropy));
- return(FinalizeCCTx(0,cp,mtx,fundingpk,txfee,EncodeDiceOpRet(funcid,sbits,fundingtxid,hentropy,hentropyproof)));
- } else fprintf(stderr,"iswin.%d does not match.%d\n",iswin,winlosetimeout);
- }
- else
- {
- *resultp = -1;
- fprintf(stderr,"iswin.%d winlosetimeout.%d\n",iswin,winlosetimeout);
- return("");
- }
- }
- *resultp = -1;
- return("couldnt find bettx or entropytx");
-}
-
-static uint256 dealer0_fundingtxid;
-void *dealer0_loop(void *_arg)
-{
- char *planstr = (char *)_arg;
- CTransaction tx,*entropytxs,entropytx; CPubKey mypk,dicepk; uint64_t entropyval; uint256 hashBlock,entropytxid,txid; int32_t height,lastht,numentropytxs,i,n,m,num; CScript fundingPubKey; struct CCcontract_info *cp,C; char coinaddr[64]; std::string res; int64_t minbet,maxbet,maxodds,timeoutblocks; uint64_t refsbits,txfee = 10000;
- if ( (cp= Diceinit(fundingPubKey,dealer0_fundingtxid,&C,planstr,txfee,mypk,dicepk,refsbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 )
- {
- fprintf(stderr,"error initializing dealer0_loop\n");
- StartShutdown();
- }
- fprintf(stderr,"dealer0 node running\n");
- height = lastht = 0;
- entropytxs = (CTransaction *)calloc(sizeof(*entropytxs),DICE_MINUTXOS);
- while ( 1 )
- {
- while ( HUSH_INSYNC == 0 || (height= HUSH_INSYNC) == lastht )
- {
- sleep(3);
- }
- lastht = height;
- fprintf(stderr,"New height.%d\n",height);
- DicePlanFunds(entropyval,entropytxid,refsbits,cp,dicepk,dealer0_fundingtxid,numentropytxs,false);
- if ( numentropytxs < DICE_MINUTXOS )
- {
- n = sqrt(DICE_MINUTXOS - numentropytxs);
- //if ( n > 10 )
- // n = 10;
- for (i=m=0; i 64 && is_hexstr((char *)res.c_str(),0) > 64 )
- {
- if ( DecodeHexTx(tx,res) != 0 )
- {
- LOCK(cs_main);
- if ( myAddtomempool(tx) != 0 )
- {
- fprintf(stderr,"ENTROPY %s: %d of %d, %d\n",tx.GetHash().GetHex().c_str(),i,n,DICE_MINUTXOS - numentropytxs);
- RelayTransaction(tx);
- entropytxs[m++] = tx;
- } else break;
- } else break;
- } else break;
- }
- for (i=0; i n.%d\n",num,DICE_MINUTXOS,n);
- for (i=0; i > unspentOutputs;
- SetCCunspents(unspentOutputs,coinaddr,true);
- for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
- {
- txid = it->first.txhash;
- vout = (int32_t)it->first.index;
- if ( vout != 0 )
- continue;
- sum += it->second.satoshis;
- if ( myGetTransaction(txid,betTx,hashBlock) != 0 && betTx.vout.size() >= 4 && betTx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 )
- {
- if ( DecodeDiceOpRet(txid,betTx.vout[betTx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash,proof) == 'B' && sbits == refsbits )
- {
- if ( myGetTransaction(betTx.vin[0].prevout.hash,entropyTx,hashBlock) != 0 )
- {
- flag = HUSH_DEALERNODE != 0;
- if ( HUSH_DEALERNODE != 0 && scriptPubKey == fundingPubKey )
- {
- bettorentropy = DiceGetEntropy(betTx,'B');
- if ( (iswin= DiceIsWinner(hentropyproof,entropyvout,txid,betTx,entropyTx,bettorentropy,sbits,minbet,maxbet,maxodds,timeoutblocks,fundingtxid)) != 0 )
- {
- if ( iswin > 0 )
- win++;
- else if ( iswin < 0 )
- loss++;
- n++;
- DiceQueue(iswin,sbits,fundingtxid,txid,betTx,entropyvout);
- }
- }
- if ( scriptPubKey != fundingPubKey )
- {
- fprintf(stderr,"serialized bettxid %d: iswin.%d W.%d L.%d %s/v%d (%c %.8f) %.8f\n",n,iswin,win,loss,txid.GetHex().c_str(),vout,funcid,(double)it->second.satoshis/COIN,(double)sum/COIN);
- res = DiceBetFinish(funcid,entropyused,entropyvout,&result,txfee,planstr,fundingtxid,txid,scriptPubKey == fundingPubKey,zeroid,-1);
- if ( result > 0 )
- {
- mySenddicetransaction(res,entropyused,entropyvout,txid,betTx,funcid,0);
- n++;
- if ( n > 10 )
- break;
- }
- }
- } else fprintf(stderr,"bettxid.%s cant find entropyTx.%s\n",txid.GetHex().c_str(),betTx.vin[0].prevout.hash.GetHex().c_str());
- }
- }
- }
- if ( didinit == 0 && HUSH_DEALERNODE == 0 && scriptPubKey == fundingPubKey )
- {
- strcpy(_planstr,planstr);
- dealer0_fundingtxid = fundingtxid;
- if ( pthread_create((pthread_t *)malloc(sizeof(pthread_t)),NULL,dealer0_loop,_planstr) == 0 )
- didinit = 1;
- }
- return(n);
- }
- else
- {
- char str[65];
- if ( (vout= myIsutxo_spent(spenttxid,bettxid,1)) >= 0 )
- {
- //fprintf(stderr,"bettx is spent\n");
- if ( myGetTransaction(bettxid,betTx,hashBlock) != 0 && myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() > 2 )
- {
- //fprintf(stderr,"found spenttxid %s\n",uint256_str(str,spenttxid));
- if ( betTx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 || betTx.vout[2].scriptPubKey.IsPayToCryptoCondition() != 0 || spenttx.vout[2].scriptPubKey != betTx.vout[2].scriptPubKey )
- return(0.);
- else return((double)spenttx.vout[2].nValue/COIN);
- }
- CCerror = "couldnt find bettx or spenttx %s\n",uint256_str(str,spenttxid);
- return(-1.);
- }
- else if ( scriptPubKey == fundingPubKey )
- res = DiceBetFinish(funcid,entropyused,entropyvout,&result,txfee,planstr,fundingtxid,bettxid,1,zeroid,-1);
- else res = DiceBetFinish(funcid,entropyused,entropyvout,&result,txfee,planstr,fundingtxid,bettxid,0,zeroid,-1);
- if ( result > 0 )
- {
- mySenddicetransaction(res,entropyused,entropyvout,bettxid,betTx,funcid,0);
- sleep(1);
- if ( (vout= myIsutxo_spent(spenttxid,bettxid,1)) >= 0 )
- {
- if ( myGetTransaction(txid,betTx,hashBlock) != 0 && myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() >= 2 )
- {
- if ( funcid == 'L' )//betTx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 || betTx.vout[2].scriptPubKey.IsPayToCryptoCondition() != 0 || spenttx.vout[2].scriptPubKey != betTx.vout[2].scriptPubKey )
- //if ( spenttx.vout[2].scriptPubKey == fundingPubKey || ((uint8_t *)spenttx.vout[2].scriptPubKey.data())[0] == 0x6a )
- return(0.);
- else return((double)spenttx.vout[2].nValue/COIN);
- } else return(0.);
- }
- CCerror = "didnt find dicefinish tx";
- } else CCerror = res;
- return(-1.);
- }
- return(0.);
-}
diff --git a/src/cc/eval.cpp b/src/cc/eval.cpp
index e490781b2..c4b3cde79 100644
--- a/src/cc/eval.cpp
+++ b/src/cc/eval.cpp
@@ -103,6 +103,7 @@ bool Eval::Dispatch(const CC *cond, const CTransaction &txTo, unsigned int nIn)
switch ( ecode )
{
+ /*
case EVAL_IMPORTPAYOUT:
return ImportPayout(vparams, txTo, nIn);
break;
@@ -110,7 +111,7 @@ bool Eval::Dispatch(const CC *cond, const CTransaction &txTo, unsigned int nIn)
case EVAL_IMPORTCOIN:
return ImportCoin(vparams, txTo, nIn);
break;
-
+ */
default:
return(ProcessCC(cp,this, vparams, txTo, nIn));
break;
diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp
deleted file mode 100644
index ad1364c11..000000000
--- a/src/cc/gamescc.cpp
+++ /dev/null
@@ -1,1813 +0,0 @@
-// Copyright (c) 2016-2023 The Hush developers
-// Distributed under the GPLv3 software license, see the accompanying
-// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
-/******************************************************************************
- * Copyright © 2014-2019 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 "gamescc.h"
-#ifdef BUILD_PRICES
-#include "games/prices.c"
-#else
-#include "games/tetris.c"
-#endif
-
-int32_t GAMEDATA(struct games_player *P,void *ptr);
-
-uint64_t _games_rngnext(uint64_t initseed)
-{
- uint16_t seeds[4]; int32_t i;
- seeds[0] = initseed;
- seeds[1] = (initseed >> 16);
- seeds[2] = (initseed >> 32);
- seeds[3] = (initseed >> 48);
- seeds[0] = (seeds[0]*GAMES_RNGMULT + GAMES_RNGOFFSET);
- seeds[1] = ((seeds[0] ^ seeds[1])*GAMES_RNGMULT + GAMES_RNGOFFSET);
- seeds[2] = ((seeds[0] ^ seeds[1] ^ seeds[2])*GAMES_RNGMULT + GAMES_RNGOFFSET);
- seeds[3] = ((seeds[0] ^ seeds[1] ^ seeds[2] ^ seeds[3])*GAMES_RNGMULT + GAMES_RNGOFFSET);
- return(((uint64_t)seeds[3] << 48) | ((uint64_t)seeds[2] << 24) | ((uint64_t)seeds[1] << 16) | seeds[0]);
-}
-
-gamesevent games_revendian(gamesevent revx)
-{
- int32_t i; gamesevent x = 0;
- //fprintf(stderr,"%04x -> ",revx);
- for (i=0; iguiflag == 0 )
- {
- static uint32_t counter;
- if ( rs->ind < rs->numkeys )
- {
- ch = rs->keystrokes[rs->ind++];
- if ( 0 )
- {
- static FILE *fp; static int32_t counter;
- if ( fp == 0 )
- fp = fopen("log","wb");
- if ( fp != 0 )
- {
- fprintf(fp,"%d: (%c) seed.%llu\n",counter,c,(long long)rs->origseed);
- fflush(fp);
- counter++;
- }
- }
- return(ch);
- }
- if ( rs->replaydone != 0 && counter++ < 3 )
- fprintf(stderr,"replay finished but readchar called\n");
- rs->replaydone = (uint32_t)time(NULL);
- return(0);
- }
- if ( rs == 0 || rs->guiflag != 0 )
- {
- c = getch();
- switch ( c )
- {
- case KEY_LEFT:
- c = 'h';
- break;
- case KEY_RIGHT:
- c = 'l';
- break;
- case KEY_UP:
- c = 'k';
- break;
- case KEY_DOWN:
- c = 'j';
- break;
- }
- ch = c;
- if (ch == 3)
- {
- //_quit();
- return(27);
- }
- } else fprintf(stderr,"readchar rs.%p non-gui error?\n",rs);
- return(ch);
-}
-
-int32_t games_replay2(uint8_t *newdata,uint64_t seed,gamesevent *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis)
-{
- struct games_state *rs; FILE *fp; int32_t i,n; void *ptr;
- rs = (struct games_state *)calloc(1,sizeof(*rs));
- rs->seed = rs->origseed = seed;
- rs->keystrokes = keystrokes;
- rs->numkeys = num;
- rs->sleeptime = sleepmillis * 1000;
- if ( player != 0 )
- {
- rs->P = *player;
- rs->restoring = 1;
- if ( rs->P.packsize > MAXPACK )
- rs->P.packsize = MAXPACK;
- }
- globalR = *rs;
- uint32_t starttime = (uint32_t)time(NULL);
- ptr = gamesiterate(rs);
- if ( 0 )
- {
- fprintf(stderr,"elapsed %d seconds\n",(uint32_t)time(NULL) - starttime);
- sleep(2);
- starttime = (uint32_t)time(NULL);
- for (i=0; i<10000; i++)
- {
- memset(rs,0,sizeof(*rs));
- rs->seed = rs->origseed = seed;
- rs->keystrokes = keystrokes;
- rs->numkeys = num;
- rs->sleeptime = 0;
- gamesiterate(rs);
- }
- fprintf(stderr,"elapsed %d seconds\n",(uint32_t)time(NULL)-starttime);
- sleep(3);
- }
- // extract playerdata
-
- /*if ( (fp= fopen("checkfile","wb")) != 0 )
- {
- //save_file(rs,fp,0);
- if ( newdata != 0 && rs->playersize > 0 )
- memcpy(newdata,rs->playerdata,rs->playersize);
- }*/
- if ( ptr != 0 )
- {
- // extract data from ptr
- if ( GAMEDATA(&rs->P,ptr) < 0 )
- memset(&rs->P,0,sizeof(rs->P));
- else
- {
- rs->playersize = sizeof(rs->P);
- if ( newdata != 0 )
- memcpy(newdata,&rs->P,rs->playersize);
- }
- free(ptr);
- }
- n = rs->playersize;
- //fprintf(stderr,"gold.%d\n",rs->P.gold); sleep(3);
- free(rs);
- return(n);
-}
-
-#ifndef STANDALONE
-#ifdef BUILD_PRICES
-#include "games/prices.cpp"
-#else
-#include "games/tetris.cpp"
-#endif
-
-void GAMEJSON(UniValue &obj,struct games_player *P);
-
-/*
-./c cclib rng 17 \"[%229433dc3749aece1bd568f374a45da3b0bc6856990d7da3cd175399577940a775%22,250]\"
-{
- "playerid": 250,
- "seed": 1398876319979341887,
- "rng": 14565767519458298868,
- "lastrng": 15075236803740723044,
- "maxrngs": 10000,
- "result": "success"
-}
-
- ./c cclib rngnext 17 \"[14565767519458298868]\"
- {
- "seed": 14565767519458297856,
- "rng": 4253087318999719449,
- "result": "success"
- }
-
- The idea is for a game to start with a near future blockhash, so the lobby gets players signed up until just prior to the designated height. then that blockhash can be used to create a stream of rngs.
-
- the same initial rng can be used for all players, if the identical starting condition is required. up to 255 different initial rng can be derived from a single blockhash. (Actually any number is possible, for simplicity rng rpc limits to 255).
-
- you will notice maxrngs and lastrng, the lastrng is the rng value that will happen after maxrng iterations of calling rngnext with the current rng. This allows making time based multiplayer games where all the nodes can validate all the other nodes rng, even without realtime synchronization of all user input events.
-
- Every time period, all players would set their rng value to the lastrng value. The only thing to be careful of is it not exceed the maxrng calls to rngnext in a single time period. otherwise the same set of rng numbers will be repeated.
-
- events rpc is called after each user event and broadcasts to the network the tuple (gametxid, pk, eventid, payload). This allows the network to verify realtime user events from a specific pk/gametxid and also to make sure no user events were changed. In the event the events were changed, then the guilty pk will be blacklisted. If no realtime events, then the guilty pk would be penalized.
-
- ./c cclib events 17 \"[%226c%22,%229433dc3749aece1bd568f374a45da3b0bc6856990d7da3cd175399577940a775%22,0]\"
- ./c cclib events 17 \"[%226d%22,%229433dc3749aece1bd568f374a45da3b0bc6856990d7da3cd175399577940a775%22,1]\"
-*/
-
-
-CScript games_newgameopret(int64_t buyin,int32_t maxplayers)
-{
- CScript opret; uint8_t evalcode = EVAL_GAMES;
- opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'G' << buyin << maxplayers);
- return(opret);
-}
-
-uint8_t games_newgameopreturndecode(int64_t &buyin,int32_t &maxplayers,CScript scriptPubKey)
-{
- std::vector vopret; uint8_t e,f;
- GetOpReturnData(scriptPubKey,vopret);
- if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> buyin; ss >> maxplayers) != 0 && e == EVAL_GAMES && f == 'G' )
- {
- return(f);
- }
- return(0);
-}
-
-CScript games_registeropret(uint256 gametxid,uint256 playertxid)
-{
- CScript opret; uint8_t evalcode = EVAL_GAMES;
- //fprintf(stderr,"opret.(%s %s).R\n",gametxid.GetHex().c_str(),playertxid.GetHex().c_str());
- opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'R' << gametxid << playertxid);
- return(opret);
-}
-
-CScript games_keystrokesopret(uint256 gametxid,uint256 batontxid,CPubKey pk,std::vectorkeystrokes)
-{
- CScript opret; uint8_t evalcode = EVAL_GAMES;
- opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'K' << gametxid << batontxid << pk << keystrokes);
- return(opret);
-}
-
-uint8_t games_keystrokesopretdecode(uint256 &gametxid,uint256 &batontxid,CPubKey &pk,std::vector &keystrokes,CScript scriptPubKey)
-{
- std::vector vopret; uint8_t e,f;
- GetOpReturnData(scriptPubKey,vopret);
- if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> gametxid; ss >> batontxid; ss >> pk; ss >> keystrokes) != 0 && e == EVAL_GAMES && f == 'K' )
- {
- return(f);
- }
- return(0);
-}
-
-uint8_t games_registeropretdecode(uint256 &gametxid,uint256 &tokenid,uint256 &playertxid,CScript scriptPubKey)
-{
- std::string name, description; std::vector vorigPubkey;
- std::vector> oprets;
- std::vector vopretNonfungible, vopret, vopretDummy,origpubkey;
- uint8_t e, f,*script; std::vector voutPubkeys;
- tokenid = zeroid;
- GetOpReturnData(scriptPubKey, vopret);
- script = (uint8_t *)vopret.data();
- if ( script[1] == 'c' && (f= DecodeTokenCreateOpRet(scriptPubKey,origpubkey,name,description,oprets)) == 'c' )
- {
- GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vopretNonfungible);
- vopret = vopretNonfungible;
- }
- else if ( script[1] != 'R' && (f= DecodeTokenOpRet(scriptPubKey, e, tokenid, voutPubkeys, oprets)) != 0 )
- {
- GetOpretBlob(oprets, OPRETID_ROGUEGAMEDATA, vopretDummy); // blob from non-creation tx opret
- vopret = vopretDummy;
- }
- if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> gametxid; ss >> playertxid) != 0 && e == EVAL_GAMES && f == 'R' )
- {
- return(f);
- }
- //fprintf(stderr,"e.%d f.%c game.%s playertxid.%s\n",e,f,gametxid.GetHex().c_str(),playertxid.GetHex().c_str());
- return(0);
-}
-
-CScript games_finishopret(uint8_t funcid,uint256 gametxid,int32_t regslot,CPubKey pk,std::vectorplayerdata,std::string pname)
-{
- CScript opret; uint8_t evalcode = EVAL_GAMES; std::string symbol(SMART_CHAIN_SYMBOL);
- opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << gametxid << symbol << pname << regslot << pk << playerdata );
- return(opret);
-}
-
-uint8_t games_finishopretdecode(uint256 &gametxid, uint256 &tokenid, int32_t ®slot, CPubKey &pk, std::vector &playerdata, std::string &symbol, std::string &pname,CScript scriptPubKey)
-{
- std::string name, description; std::vector vorigPubkey;
- std::vector> oprets, opretsDummy;
- std::vector vopretNonfungible, vopret, vopretDummy,origpubkey;
- uint8_t e, f,*script; std::vector voutPubkeys;
- tokenid = zeroid;
- GetOpReturnData(scriptPubKey, vopret);
- script = (uint8_t *)vopret.data();
- if ( script[1] == 'c' && (f= DecodeTokenCreateOpRet(scriptPubKey,origpubkey,name,description, oprets)) == 'c' )
- {
- GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vopretNonfungible);
- vopret = vopretNonfungible;
- }
- else if ( script[1] != 'H' && script[1] != 'Q' && (f= DecodeTokenOpRet(scriptPubKey, e, tokenid, voutPubkeys, opretsDummy)) != 0 )
- {
- //fprintf(stderr,"decode opret %c tokenid.%s\n",script[1],tokenid.GetHex().c_str());
- GetNonfungibleData(tokenid, vopretNonfungible); //load nonfungible data from the 'tokenbase' tx
- vopret = vopretNonfungible;
- }
- if ( vopret.size() > 2 && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> gametxid; ss >> symbol; ss >> pname; ss >> regslot; ss >> pk; ss >> playerdata) != 0 && e == EVAL_GAMES && (f == 'H' || f == 'Q') )
- {
- return(f);
- }
- fprintf(stderr,"SKIP obsolete: e.%d f.%c game.%s regslot.%d psize.%d\n",e,f,gametxid.GetHex().c_str(),regslot,(int32_t)playerdata.size());
- return(0);
-}
-
-CScript games_eventopret(uint32_t timestamp,CPubKey pk,std::vector sig,std::vector payload)
-{
- CScript opret; uint8_t evalcode = EVAL_GAMES;
- opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'E' << timestamp << pk << sig << payload);
- return(opret);
-}
-
-uint8_t games_eventdecode(uint32_t ×tamp,CPubKey &pk,std::vector &sig,std::vector &payload,std::vector vopret)
-{
- uint8_t e,f;
- timestamp = 0;
- if ( vopret.size() > 6 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> timestamp; ss >> pk; ss >> sig; ss >> payload) != 0 && e == EVAL_GAMES )
- {
- return(f);
- }
- fprintf(stderr,"ERROR e.%d f.%d pk.%d sig.%d payload.%d\n",e,f,(int32_t)pk.size(),(int32_t)sig.size(),(int32_t)payload.size());
- return(0);
-}
-
-UniValue games_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastflag)
-{
- CTransaction tx;
- if ( rawtx.size() > 0 )
- {
- result.push_back(Pair("hex",rawtx));
- if ( DecodeHexTx(tx,rawtx) != 0 )
- {
- if ( broadcastflag != 0 && myAddtomempool(tx) != 0 )
- RelayTransaction(tx);
- result.push_back(Pair("txid",tx.GetHash().ToString()));
- result.push_back(Pair("result","success"));
- } else result.push_back(Pair("error","decode hex"));
- } else result.push_back(Pair("error","couldnt finalize CCtx"));
- return(result);
-}
-
-UniValue games_rngnext(uint64_t txfee,struct CCcontract_info *cp,cJSON *params)
-{
- UniValue result(UniValue::VOBJ); int32_t n; uint64_t seed;
- if ( params != 0 && (n= cJSON_GetArraySize(params)) == 1 )
- {
- seed = jdouble(jitem(params,0),0);
- result.push_back(Pair("seed",seed));
- seed = _games_rngnext(seed);
- result.push_back(Pair("rng",seed));
- result.push_back(Pair("result","success"));
- }
- else
- {
- result.push_back(Pair("result","error"));
- result.push_back(Pair("error","not enough params"));
- }
- return(result);
-}
-
-UniValue games_rng(uint64_t txfee,struct CCcontract_info *cp,cJSON *params)
-{
- UniValue result(UniValue::VOBJ); int32_t i,n,playerid=0; uint64_t seed=0,initseed; bits256 hash;
- if ( params != 0 && ((n= cJSON_GetArraySize(params)) == 1 || n == 2) )
- {
- hash = jbits256(jitem(params,0),0);
- if ( n == 2 )
- {
- playerid = juint(jitem(params,1),0);
- if ( playerid >= 0xff )
- {
- result.push_back(Pair("result","error"));
- result.push_back(Pair("error","playerid too big"));
- return(result);
- }
- }
- playerid++;
- for (i=0; i<8; i++)
- {
- if ( ((1 << i) & playerid) != 0 )
- seed ^= (uint64_t)hash.uints[i] << ((i&1)*32);
- }
- initseed = seed;
- seed = _games_rngnext(initseed);
- result.push_back(Pair("playerid",(int64_t)(playerid - 1)));
- result.push_back(Pair("seed",initseed));
- result.push_back(Pair("rng",seed));
- for (i=0; i &sig,std::vector payload,CPubKey pk)
-{
- static secp256k1_context *ctx;
- size_t siglen = 74; secp256k1_pubkey pubkey; secp256k1_ecdsa_signature signature; int32_t len,verifyflag = 1,retval=-100; uint8_t privkey[32]; uint256 hash; uint32_t t;
- if ( ctx == 0 )
- ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
- if ( ctx != 0 )
- {
- Myprivkey(privkey);
- len = payload.size();
- payload.resize(len + 4);
- if ( timestamp == 0 )
- {
- timestamp = (uint32_t)time(NULL);
- verifyflag = 0;
- }
- t = timestamp;
- payload[len++] = t, t >>= 8;
- payload[len++] = t, t >>= 8;
- payload[len++] = t, t >>= 8;
- payload[len++] = t;
- vcalc_sha256(0,(uint8_t *)&hash,&payload[0],len);
- if ( verifyflag == 0 )
- {
- if ( secp256k1_ecdsa_sign(ctx,&signature,(uint8_t *)&hash,privkey,NULL,NULL) > 0 )
- {
- sig.resize(siglen);
- if ( secp256k1_ecdsa_signature_serialize_der(ctx,&sig[0],&siglen,&signature) > 0 )
- {
- if ( siglen != sig.size() )
- sig.resize(siglen);
- retval = 0;
- } else retval = -3;
- } else retval = -2;
- }
- else
- {
- if ( secp256k1_ec_pubkey_parse(ctx,&pubkey,pk.begin(),33) > 0 )
- {
- if ( secp256k1_ecdsa_signature_parse_der(ctx,&signature,&sig[0],sig.size()) > 0 )
- {
- if ( secp256k1_ecdsa_verify(ctx,&signature,(uint8_t *)&hash,&pubkey) > 0 )
- retval = 0;
- else retval = -4;
- } else retval = -3;
- } else retval = -2;
- }
- } else retval = -1;
- memset(privkey,0,sizeof(privkey));
- return(retval);
-}
-
-int32_t games_event(uint32_t timestamp,uint256 gametxid,int32_t eventid,std::vector payload)
-{
- std::vector sig,vopret; CPubKey mypk; uint32_t x; int32_t i,len = payload.size();
- payload.resize(len + 4 + 32);
- for (i=0; i<32; i++)
- payload[len++] = ((uint8_t *)&gametxid)[i];
- x = eventid;
- payload[len++] = x, x >>= 8;
- payload[len++] = x, x >>= 8;
- payload[len++] = x, x >>= 8;
- payload[len++] = x;
- mypk = pubkey2pk(Mypubkey());
- if ( games_eventsign(timestamp,sig,payload,mypk) == 0 )
- {
- GetOpReturnData(games_eventopret(timestamp,mypk,sig,payload),vopret);
- games_payloadrecv(mypk,timestamp,payload);
- hush_sendmessage(4,8,"events",vopret);
- return(0);
- }
- fprintf(stderr,"games_eventsign error\n");
- return(-1);
-}
-
-UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params)
-{
- static uint256 lastgametxid; static uint32_t numevents;
- UniValue result(UniValue::VOBJ); std::vector payload; int32_t len,i,n; uint32_t x; CPubKey mypk; char str[67]; uint32_t eventid,timestamp = 0; uint256 gametxid;
- if ( params != 0 && (n= cJSON_GetArraySize(params)) >= 1 && n <= 3 )
- {
- mypk = pubkey2pk(Mypubkey());
- if ( payments_parsehexdata(payload,jitem(params,0),0) == 0 )
- {
- if ( n >= 2 )
- gametxid = juint256(jitem(params,1));
- else gametxid = zeroid;
- if ( gametxid != lastgametxid )
- {
- lastgametxid = gametxid;
- numevents = 1;
- eventid = 0;
- }
- if ( n == 3 )
- {
- eventid = juint(jitem(params,2),0);
- if ( numevents <= eventid )
- numevents = eventid+1;
- } else eventid = numevents++;
- if ( games_event(timestamp,gametxid,eventid,payload) == 0 )
- {
- result.push_back(Pair("gametxid",gametxid.GetHex()));
- result.push_back(Pair("eventid",(int64_t)eventid));
- result.push_back(Pair("payload",jstr(jitem(params,0),0)));
- result.push_back(Pair("timestamp",(int64_t)timestamp));
- result.push_back(Pair("result","success"));
- result.push_back(Pair("pubkey33",pubkey33_str(str,(uint8_t *)&mypk)));
- }
- else
- {
- result.push_back(Pair("result","error"));
- result.push_back(Pair("error","signing ereror"));
- }
- }
- else
- {
- result.push_back(Pair("result","error"));
- result.push_back(Pair("error","couldnt parsehexdata"));
- }
- }
- else
- {
- result.push_back(Pair("result","error"));
- result.push_back(Pair("error","not enough params"));
- }
- return(result);
-}
-
-void hush_netevent(std::vector message)
-{
- int32_t i,retval,lag,lagerr=0; uint32_t timestamp,now; CPubKey pk; std::vector sig,payload; char str[67];
- if ( games_eventdecode(timestamp,pk,sig,payload,message) == 'E' )
- {
- now = (uint32_t)time(NULL);
- lag = now - timestamp;
- if ( lag < -3 || lag > 3 )
- {
- fprintf(stderr,"LAG ERROR ");
- lagerr = lag;
- }
- if ( (retval= games_eventsign(timestamp,sig,payload,pk)) != 0 )
- fprintf(stderr,"SIG ERROR.%d ",retval);
- else if ( lagerr == 0 )
- {
- if ( games_payloadrecv(pk,timestamp,payload) == 0 ) // first time this is seen
- {
- if ( (rand() % 10) == 0 )
- {
- //fprintf(stderr,"relay message.[%d]\n",(int32_t)message.size());
- hush_sendmessage(2,2,"events",message);
- }
- }
- }
- //for (i=0; i 0 )
- result.push_back(Pair("maxplayers",maxplayers));
- if ( buyin >= 0 )
- {
- result.push_back(Pair("buyin",ValueFromAmount(buyin)));
- if ( buyin == 0 )
- result.push_back(Pair("type","newbie"));
- else result.push_back(Pair("type","buyin"));
- }
-}
-
-int32_t games_isvalidgame(struct CCcontract_info *cp,int32_t &gameheight,CTransaction &tx,int64_t &buyin,int32_t &maxplayers,uint256 txid,int32_t unspentv0)
-{
- uint256 hashBlock; int32_t i,numvouts; char coinaddr[64]; CPubKey gamespk; uint64_t txfee = 10000;
- buyin = maxplayers = 0;
- if ( (txid == zeroid || myGetTransaction(txid,tx,hashBlock) != 0) && (numvouts= tx.vout.size()) > 1 )
- {
- if ( txid != zeroid )
- gameheight = hush_blockheight(hashBlock);
- else
- {
- txid = tx.GetHash();
- //fprintf(stderr,"set txid %s %llu\n",txid.GetHex().c_str(),(long long)CCgettxout(txid,0,1));
- }
- if ( IsCClibvout(cp,tx,0,cp->unspendableCCaddr) == txfee && (unspentv0 == 0 || CCgettxout(txid,0,1,0) == txfee) )
- {
- if ( games_newgameopreturndecode(buyin,maxplayers,tx.vout[numvouts-1].scriptPubKey) == 'G' )
- {
- if ( maxplayers < 1 || maxplayers > GAMES_MAXPLAYERS || buyin < 0 )
- return(-6);
- if ( numvouts > 2*maxplayers+1 )
- {
- for (i=0; i= 0 )
- txid = spenttxid;
- else if ( myIsutxo_spentinmempool(spenttxid,spentvini,txid,vout) == 0 || spenttxid == zeroid )
- {
- fprintf(stderr,"mempool tracking error %s/v0\n",txid.ToString().c_str());
- break;
- }
- txid = spenttxid;
- vout = 0;
- //fprintf(stderr,"n.%d next txid.%s/v%d\n",n,txid.GetHex().c_str(),spentvini);
- if ( spentvini != 0 )
- break;
- if ( n++ > GAMES_MAXITERATIONS )
- break;
- }
- if ( txid != zeroid )
- {
- if ( myGetTransaction(txid,tx,hashBlock) != 0 )
- {
- if ( (pindex= hush_blockindex(hashBlock)) != 0 )
- {
- if ( pindex->GetHeight() <= gameht+GAMES_MAXKEYSTROKESGAP )
- alive++;
- }
- }
- }
- }
- }
- else if ( registration_open != 0 )
- openslots++;
- }
- //fprintf(stderr,"numalive.%d openslots.%d\n",alive,openslots);
- return(alive);
-}
-
-UniValue games_playerobj(std::vector playerdata,uint256 playertxid,uint256 tokenid,std::string symbol,std::string pname,uint256 gametxid)
-{
- int32_t i,vout,spentvini,numvouts,n=0; uint256 txid,spenttxid,hashBlock; struct games_player P; char packitemstr[512],*datastr=0; UniValue obj(UniValue::VOBJ),a(UniValue::VARR); CTransaction tx;
- memset(&P,0,sizeof(P));
- if ( playerdata.size() > 0 )
- {
- datastr = (char *)malloc(playerdata.size()*2+1);
- for (i=0; i= 0 )
- txid = spenttxid;
- else if ( myIsutxo_spentinmempool(spenttxid,spentvini,txid,vout) == 0 || spenttxid == zeroid )
- {
- fprintf(stderr,"mempool tracking error %s/v0\n",txid.ToString().c_str());
- break;
- }
- txid = spenttxid;
- vout = 0;
- if ( myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 1 )
- {
- for (i=0; i GAMES_MAXITERATIONS )
- break;
- }
- obj.push_back(Pair("gametxid",gametxid.GetHex()));
- if ( txid != playertxid )
- obj.push_back(Pair("batontxid",txid.GetHex()));
- obj.push_back(Pair("playertxid",playertxid.GetHex()));
- if ( tokenid != zeroid )
- obj.push_back(Pair("tokenid",tokenid.GetHex()));
- else obj.push_back(Pair("tokenid",playertxid.GetHex()));
- if ( datastr != 0 )
- {
- obj.push_back(Pair("data",datastr));
- free(datastr);
- }
- obj.push_back(Pair("pack",a));
- GAMEPLAYERJSON(obj,&P);
- obj.push_back(Pair("chain",symbol));
- obj.push_back(Pair("pname",pname));
- return(obj);
-}
-
-int32_t games_iterateplayer(uint256 ®istertxid,uint256 firsttxid,int32_t firstvout,uint256 lasttxid) // retrace playertxid vins to reach highlander <- this verifies player is valid and rogue_playerdataspend makes sure it can only be used once
-{
- uint256 spenttxid,txid = firsttxid; int32_t spentvini,n,vout = firstvout;
- registertxid = zeroid;
- if ( vout < 0 )
- return(-1);
- n = 0;
- while ( (spentvini= myIsutxo_spent(spenttxid,txid,vout)) == 0 )
- {
- txid = spenttxid;
- vout = spentvini;
- if ( registertxid == zeroid )
- registertxid = txid;
- if ( ++n >= GAMES_MAXITERATIONS )
- {
- fprintf(stderr,"games_iterateplayer n.%d, seems something is wrong\n",n);
- return(-2);
- }
- }
- if ( txid == lasttxid )
- return(0);
- else
- {
- fprintf(stderr,"firsttxid.%s/v%d -> %s != last.%s\n",firsttxid.ToString().c_str(),firstvout,txid.ToString().c_str(),lasttxid.ToString().c_str());
- return(-1);
- }
-}
-
-int32_t games_playerdata(struct CCcontract_info *cp,uint256 &origplayergame,uint256 &tokenid,CPubKey &pk,std::vector &playerdata,std::string &symbol,std::string &pname,uint256 playertxid)
-{
- uint256 origplayertxid,hashBlock,gametxid,registertxid; CTransaction gametx,playertx,highlandertx; std::vector vopret; uint8_t *script,e,f; int32_t i,regslot,gameheight,numvouts,maxplayers; int64_t buyin;
- if ( myGetTransaction(playertxid,playertx,hashBlock) != 0 && (numvouts= playertx.vout.size()) > 0 )
- {
- if ( (f= games_finishopretdecode(gametxid,tokenid,regslot,pk,playerdata,symbol,pname,playertx.vout[numvouts-1].scriptPubKey)) == 'H' || f == 'Q' )
- {
- origplayergame = gametxid;
- if ( tokenid != zeroid )
- {
- playertxid = tokenid;
- if ( myGetTransaction(playertxid,playertx,hashBlock) == 0 || (numvouts= playertx.vout.size()) <= 0 )
- {
- fprintf(stderr,"couldnt get tokenid.%s\n",playertxid.GetHex().c_str());
- return(-2);
- }
- }
- if ( games_isvalidgame(cp,gameheight,gametx,buyin,maxplayers,gametxid,0) == 0 )
- {
- //fprintf(stderr,"playertxid.%s got vin.%s/v%d gametxid.%s iterate.%d\n",playertxid.ToString().c_str(),playertx.vin[1].prevout.hash.ToString().c_str(),(int32_t)playertx.vin[1].prevout.n-maxplayers,gametxid.ToString().c_str(),games_iterateplayer(registertxid,gametxid,playertx.vin[1].prevout.n-maxplayers,playertxid));
- if ( (tokenid != zeroid || playertx.vin[1].prevout.hash == gametxid) && games_iterateplayer(registertxid,gametxid,playertx.vin[1].prevout.n-maxplayers,playertxid) == 0 )
- {
- // if registertxid has vin from pk, it can be used
- return(0);
- } else fprintf(stderr,"hash mismatch or illegal gametxid\n");
- } else fprintf(stderr,"invalid game %s\n",gametxid.GetHex().c_str());
- } //else fprintf(stderr,"invalid player funcid.%c\n",f);
- } else fprintf(stderr,"couldnt get playertxid.%s\n",playertxid.GetHex().c_str());
- return(-1);
-}
-
-int32_t games_iamregistered(int32_t maxplayers,uint256 gametxid,CTransaction tx,char *mygamesaddr)
-{
- int32_t i,vout; uint256 spenttxid,hashBlock; CTransaction spenttx; char destaddr[64];
- for (i=0; i= 0 )
- {
- if ( myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() > 0 )
- {
- Getscriptaddress(destaddr,spenttx.vout[0].scriptPubKey);
- if ( strcmp(mygamesaddr,destaddr) == 0 )
- return(1);
- //else fprintf(stderr,"myaddr.%s vs %s\n",mygamesaddr,destaddr);
- } //else fprintf(stderr,"cant find spenttxid.%s\n",spenttxid.GetHex().c_str());
- } //else fprintf(stderr,"vout %d is unspent\n",vout);
- }
- return(0);
-}
-
-int64_t games_buyins(uint256 gametxid,int32_t maxplayers)
-{
- int32_t i,vout; uint256 spenttxid,hashBlock; CTransaction spenttx; int64_t buyins = 0;
- for (i=0; i= 0 )
- {
- if ( myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() > 0 )
- {
- if ( spenttx.vout[0].nValue > GAMES_REGISTRATIONSIZE )
- buyins += (spenttx.vout[0].nValue - GAMES_REGISTRATIONSIZE);
- } //else fprintf(stderr,"cant find spenttxid.%s\n",spenttxid.GetHex().c_str());
- } //else fprintf(stderr,"vout %d is unspent\n",vout);
- }
- return(buyins);
-}
-
-uint64_t games_gamefields(UniValue &obj,int64_t maxplayers,int64_t buyin,uint256 gametxid,char *mygamesaddr)
-{
- CBlockIndex *pindex; int32_t ht,openslots,delay,numplayers; uint256 hashBlock; uint64_t seed=0; char cmd[512]; CTransaction tx;
- if ( myGetTransaction(gametxid,tx,hashBlock) != 0 && (pindex= hush_blockindex(hashBlock)) != 0 )
- {
- ht = pindex->GetHeight();
- delay = GAMES_REGISTRATION * (maxplayers > 1);
- obj.push_back(Pair("height",ht));
- obj.push_back(Pair("start",ht+delay));
- if ( hush_nextheight() > ht+delay )
- {
- if ( (pindex= hush_chainactive(ht+delay)) != 0 )
- {
- hashBlock = pindex->GetBlockHash();
- obj.push_back(Pair("starthash",hashBlock.ToString()));
- memcpy(&seed,&hashBlock,sizeof(seed));
- seed &= (1LL << 62) - 1;
- obj.push_back(Pair("seed",(int64_t)seed));
- if ( games_iamregistered(maxplayers,gametxid,tx,mygamesaddr) > 0 )
- sprintf(cmd,"cc/%s %llu %s",GAMENAME,(long long)seed,gametxid.ToString().c_str());
- else sprintf(cmd,"./hush-cli -ac_name=%s cclib register %d \"[%%22%s%%22]\"",SMART_CHAIN_SYMBOL,EVAL_GAMES,gametxid.ToString().c_str());
- obj.push_back(Pair("run",cmd));
- }
- }
- obj.push_back(Pair("alive",games_playersalive(openslots,numplayers,gametxid,maxplayers,ht,tx)));
- obj.push_back(Pair("openslots",openslots));
- obj.push_back(Pair("numplayers",numplayers));
- obj.push_back(Pair("buyins",ValueFromAmount(games_buyins(gametxid,maxplayers))));
- }
- obj.push_back(Pair("maxplayers",maxplayers));
- obj.push_back(Pair("buyin",ValueFromAmount(buyin)));
- return(seed);
-}
-
-UniValue games_playerinfo(uint64_t txfee,struct CCcontract_info *cp,cJSON *params)
-{
- UniValue result(UniValue::VOBJ); std::vector playerdata; uint256 playertxid,tokenid,origplayergame;int32_t n; CPubKey pk; bits256 t; std::string symbol,pname;
- result.push_back(Pair("result","success"));
- games_univalue(result,"playerinfo",-1,-1);
- if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 )
- {
- if ( n > 0 )
- {
- playertxid = juint256(jitem(params,0));
- if ( games_playerdata(cp,origplayergame,tokenid,pk,playerdata,symbol,pname,playertxid) < 0 )
- return(cclib_error(result,"invalid playerdata"));
- result.push_back(Pair("player",games_playerobj(playerdata,playertxid,tokenid,symbol,pname,origplayergame)));
- } else return(cclib_error(result,"no playertxid"));
- return(result);
- } else return(cclib_error(result,"couldnt reparse params"));
-}
-
-void disp_gamesplayerdata(std::vector playerdata)
-{
- struct games_player P; int32_t i; char packitemstr[512],str[512];
- if ( playerdata.size() > 0 )
- {
- for (i=0; i maxplayers+1 )
- {
- r = rand() % maxplayers;
- for (j=0; j &playerdata,uint256 &batontxid,int32_t &batonvout,int64_t &batonvalue,int32_t &batonht,uint256 gametxid,CTransaction gametx,int32_t maxplayers,char *destaddr,int32_t &numplayers,std::string &symbol,std::string &pname)
-{
- int32_t i,numvouts,spentvini,n,matches = 0; CPubKey pk; uint256 tid,active,spenttxid,tokenid,hashBlock,txid,origplayergame; CTransaction spenttx,matchtx,batontx; std::vector checkdata; CBlockIndex *pindex; char ccaddr[64]; gamesevent *keystrokes=0;
- batonvalue = numkeys = numplayers = batonht = 0;
- playertxid = batontxid = zeroid;
- if ( keystrokesp != 0 )
- *keystrokesp = 0;
- for (i=0; i= 0 )
- {
- if ( myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() > 0 )
- {
- numplayers++;
- Getscriptaddress(ccaddr,spenttx.vout[0].scriptPubKey);
- if ( strcmp(destaddr,ccaddr) == 0 )
- {
- matches++;
- regslot = i;
- matchtx = spenttx;
- } //else fprintf(stderr,"%d+1 doesnt match %s vs %s\n",i,ccaddr,destaddr);
- } //else fprintf(stderr,"%d+1 couldnt find spenttx.%s\n",i,spenttxid.GetHex().c_str());
- } //else fprintf(stderr,"%d+1 unspent\n",i);
- }
- if ( matches == 1 )
- {
- numvouts = matchtx.vout.size();
-//fprintf(stderr,"matchtxid.%s matches.%d numvouts.%d\n",matchtx.GetHash().GetHex().c_str(),matches,numvouts);
- if ( games_registeropretdecode(txid,tokenid,playertxid,matchtx.vout[numvouts-1].scriptPubKey) == 'R' )//&& txid == gametxid )
- {
- //fprintf(stderr,"tokenid.%s txid.%s vs gametxid.%s player.%s\n",tokenid.GetHex().c_str(),txid.GetHex().c_str(),gametxid.GetHex().c_str(),playertxid.GetHex().c_str());
- if ( tokenid != zeroid )
- active = tokenid;
- else active = playertxid;
- if ( active == zeroid || games_playerdata(cp,origplayergame,tid,pk,playerdata,symbol,pname,active) == 0 )
- {
- txid = matchtx.GetHash();
- //fprintf(stderr,"scan forward active.%s spenttxid.%s\n",active.GetHex().c_str(),txid.GetHex().c_str());
- n = 0;
- while ( CCgettxout(txid,0,1,0) < 0 )
- {
- spenttxid = zeroid;
- spentvini = -1;
- if ( (spentvini= myIsutxo_spent(spenttxid,txid,0)) >= 0 )
- txid = spenttxid;
- else
- {
- if ( myIsutxo_spentinmempool(spenttxid,spentvini,txid,0) == 0 || spenttxid == zeroid )
- {
- fprintf(stderr,"mempool tracking error %s/v0\n",txid.ToString().c_str());
- return(-2);
- }
- }
- txid = spenttxid;
- //fprintf(stderr,"n.%d next txid.%s/v%d\n",n,txid.GetHex().c_str(),spentvini);
- if ( spentvini != 0 ) // game is over?
- {
- //fprintf(stderr,"gameisover n.%d next txid.%s/v%d\n",n,txid.GetHex().c_str(),spentvini);
- return(0);
- }
- if ( keystrokesp != 0 && myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() >= 2 )
- {
- uint256 g,b; CPubKey p; std::vector k;
- if ( games_keystrokesopretdecode(g,b,p,k,spenttx.vout[spenttx.vout.size()-1].scriptPubKey) == 'K' )
- {
- //fprintf(stderr,"update keystrokes.%p[%d]\n",keystrokes,numkeys);
- keystrokes = (gamesevent *)realloc(keystrokes,(int32_t)(sizeof(*keystrokes)*numkeys + k.size()));
- for (i=0; i= GAMES_MAXITERATIONS )
- {
- fprintf(stderr,"games_findbaton n.%d, seems something is wrong\n",n);
- return(-5);
- }
- }
- //fprintf(stderr,"set baton %s\n",txid.GetHex().c_str());
- batontxid = txid;
- batonvout = 0; // not vini
- // how to detect timeout, bailedout, highlander
- hashBlock = zeroid;
- if ( myGetTransaction(batontxid,batontx,hashBlock) != 0 && batontx.vout.size() > 0 )
- {
- if ( hashBlock == zeroid )
- batonht = hush_nextheight();
- else if ( (pindex= hush_blockindex(hashBlock)) == 0 )
- return(-4);
- else batonht = pindex->GetHeight();
- batonvalue = batontx.vout[0].nValue;
- //printf("batonht.%d keystrokes[%d]\n",batonht,numkeys);
- return(0);
- } else fprintf(stderr,"couldnt find baton\n");
- } else fprintf(stderr,"error with playerdata\n");
- } else fprintf(stderr,"findbaton opret error\n");
- }
- return(-1);
-}
-
-void games_gameplayerinfo(struct CCcontract_info *cp,UniValue &obj,uint256 gametxid,CTransaction gametx,int32_t vout,int32_t maxplayers,char *mygamesaddr)
-{
- // identify if bailout or quit or timed out
- uint256 batontxid,spenttxid,gtxid,ptxid,tokenid,hashBlock,playertxid; CTransaction spenttx,batontx; int32_t numplayers,regslot,numkeys,batonvout,batonht,retval; int64_t batonvalue; std::vector playerdata; char destaddr[64]; std::string symbol,pname;
- destaddr[0] = 0;
- if ( myIsutxo_spent(spenttxid,gametxid,vout) >= 0 )
- {
- if ( myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() > 0 )
- Getscriptaddress(destaddr,spenttx.vout[0].scriptPubKey);
- }
- obj.push_back(Pair("slot",(int64_t)vout-1));
- if ( (retval= games_findbaton(cp,playertxid,0,numkeys,regslot,playerdata,batontxid,batonvout,batonvalue,batonht,gametxid,gametx,maxplayers,destaddr,numplayers,symbol,pname)) == 0 )
- {
- if ( CCgettxout(gametxid,maxplayers+vout,1,0) == 10000 )
- {
- if ( myGetTransaction(batontxid,batontx,hashBlock) != 0 && batontx.vout.size() > 1 )
- {
- if ( games_registeropretdecode(gtxid,tokenid,ptxid,batontx.vout[batontx.vout.size()-1].scriptPubKey) == 'R' && ptxid == playertxid && gtxid == gametxid )
- obj.push_back(Pair("status","registered"));
- else obj.push_back(Pair("status","alive"));
- } else obj.push_back(Pair("status","error"));
- } else obj.push_back(Pair("status","finished"));
- obj.push_back(Pair("baton",batontxid.ToString()));
- obj.push_back(Pair("tokenid",tokenid.ToString()));
- obj.push_back(Pair("batonaddr",destaddr));
- obj.push_back(Pair("ismine",strcmp(mygamesaddr,destaddr)==0));
- obj.push_back(Pair("batonvout",(int64_t)batonvout));
- obj.push_back(Pair("batonvalue",ValueFromAmount(batonvalue)));
- obj.push_back(Pair("batonht",(int64_t)batonht));
- if ( playerdata.size() > 0 )
- obj.push_back(Pair("player",games_playerobj(playerdata,playertxid,tokenid,symbol,pname,gametxid)));
- } else fprintf(stderr,"findbaton err.%d\n",retval);
-}
-
-UniValue games_newgame(uint64_t txfee,struct CCcontract_info *cp,cJSON *params)
-{
- CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
- UniValue result(UniValue::VOBJ); std::string rawtx; CPubKey gamespk,mypk; char *jsonstr; uint64_t inputsum,change,required,buyin=0; int32_t i,n,maxplayers = 1;
- if ( txfee == 0 )
- txfee = 10000;
- if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 )
- {
- if ( n > 0 )
- {
- maxplayers = juint(jitem(params,0),0);
- if ( n > 1 )
- buyin = jdouble(jitem(params,1),0) * COIN + 0.0000000049;
- }
- }
- if ( maxplayers < 1 || maxplayers > GAMES_MAXPLAYERS )
- return(cclib_error(result,"illegal maxplayers"));
- mypk = pubkey2pk(Mypubkey());
- gamespk = GetUnspendable(cp,0);
- games_univalue(result,"newgame",maxplayers,buyin);
- required = (3*txfee + maxplayers*(GAMES_REGISTRATIONSIZE+txfee));
- if ( (inputsum= AddCClibInputs(cp,mtx,gamespk,required,16,cp->unspendableCCaddr,1)) >= required )
- {
- mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,gamespk));
- for (i=0; ievalcode,GAMES_REGISTRATIONSIZE,gamespk,gamespk));
- for (i=0; ievalcode,txfee,gamespk,gamespk));
- if ( (change= inputsum - required) >= txfee )
- mtx.vout.push_back(MakeCC1vout(cp->evalcode,change,gamespk));
- rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,games_newgameopret(buyin,maxplayers));
- return(games_rawtxresult(result,rawtx,1));
- }
- else return(cclib_error(result,"illegal maxplayers"));
- return(result);
-}
-
-UniValue games_pending(uint64_t txfee,struct CCcontract_info *cp,cJSON *params)
-{
- UniValue result(UniValue::VOBJ),a(UniValue::VARR); int64_t buyin; uint256 txid,hashBlock; CTransaction tx; int32_t openslots,maxplayers,numplayers,gameheight,nextheight,vout,numvouts; CPubKey gamespk; char coinaddr[64];
- std::vector > unspentOutputs;
- gamespk = GetUnspendable(cp,0);
- GetCCaddress(cp,coinaddr,gamespk);
- SetCCunspents(unspentOutputs,coinaddr,true);
- nextheight = hush_nextheight();
- for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
- {
- txid = it->first.txhash;
- vout = (int32_t)it->first.index;
- //char str[65]; fprintf(stderr,"%s check %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN);
- if ( it->second.satoshis != txfee || vout != 0 ) // reject any that are not highlander markers
- continue;
- if ( games_isvalidgame(cp,gameheight,tx,buyin,maxplayers,txid,1) == 0 && nextheight <= gameheight+GAMES_MAXKEYSTROKESGAP )
- {
- games_playersalive(openslots,numplayers,txid,maxplayers,gameheight,tx);
- if ( openslots > 0 )
- a.push_back(txid.GetHex());
- }
- }
- result.push_back(Pair("result","success"));
- games_univalue(result,"pending",-1,-1);
- result.push_back(Pair("pending",a));
- result.push_back(Pair("numpending",(int64_t)a.size()));
- return(result);
-}
-
-UniValue games_gameinfo(uint64_t txfee,struct CCcontract_info *cp,cJSON *params)
-{
- UniValue result(UniValue::VOBJ),a(UniValue::VARR); int32_t i,n,gameheight,maxplayers,numvouts; uint256 txid; CTransaction tx; int64_t buyin; uint64_t seed; bits256 t; char myaddr[64],str[64]; CPubKey mypk,gamespk;
- result.push_back(Pair("name","games"));
- result.push_back(Pair("method","info"));
- if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 )
- {
- if ( n > 0 )
- {
- txid = juint256(jitem(params,0));
- result.push_back(Pair("gametxid",txid.GetHex()));
- if ( games_isvalidgame(cp,gameheight,tx,buyin,maxplayers,txid,0) == 0 )
- {
- result.push_back(Pair("result","success"));
- result.push_back(Pair("gameheight",(int64_t)gameheight));
- mypk = pubkey2pk(Mypubkey());
- gamespk = GetUnspendable(cp,0);
- GetCCaddress1of2(cp,myaddr,gamespk,mypk);
- seed = games_gamefields(result,maxplayers,buyin,txid,myaddr);
- result.push_back(Pair("seed",(int64_t)seed));
- for (i=0; i GAMES_REGISTRATIONSIZE 1of2 registration baton from creategame
- // vin1 -> optional nonfungible character vout @
- // vin2 -> original creation TCBOO playerdata used
- // vin3+ -> buyin
- // vout0 -> keystrokes/completion baton
- CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
- UniValue result(UniValue::VOBJ); char destaddr[64],coinaddr[64]; uint256 tokenid,gametxid,origplayergame,playertxid,hashBlock; int32_t err,maxplayers,gameheight,n,numvouts,vout=1; int64_t inputsum,buyin,CCchange=0; CPubKey pk,mypk,gamespk,burnpk; CTransaction tx,playertx; std::vector playerdata; std::string rawtx,symbol,pname; bits256 t;
-
- if ( txfee == 0 )
- txfee = 10000;
- mypk = pubkey2pk(Mypubkey());
- burnpk = pubkey2pk(ParseHex(CC_BURNPUBKEY));
- gamespk = GetUnspendable(cp,0);
- games_univalue(result,"register",-1,-1);
- playertxid = tokenid = zeroid;
- if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 )
- {
- if ( n > 0 )
- {
- gametxid = juint256(jitem(params,0));
- if ( (err= games_isvalidgame(cp,gameheight,tx,buyin,maxplayers,gametxid,1)) == 0 )
- {
- if ( n > 1 )
- {
- playertxid = juint256(jitem(params,1));
- if ( games_playerdata(cp,origplayergame,tokenid,pk,playerdata,symbol,pname,playertxid) < 0 )
- return(cclib_error(result,"couldnt extract valid playerdata"));
- if ( tokenid != zeroid ) // if it is tokentransfer this will be 0
- vout = 1;
- }
- if ( hush_nextheight() > gameheight + GAMES_MAXKEYSTROKESGAP )
- return(cclib_error(result,"didnt register in time, GAMES_MAXKEYSTROKESGAP"));
- games_univalue(result,0,maxplayers,buyin);
- GetCCaddress1of2(cp,coinaddr,gamespk,mypk);
- if ( games_iamregistered(maxplayers,gametxid,tx,coinaddr) > 0 )
- return(cclib_error(result,"already registered"));
- if ( (inputsum= games_registrationbaton(mtx,gametxid,tx,maxplayers)) != GAMES_REGISTRATIONSIZE )
- return(cclib_error(result,"couldnt find available registration baton"));
- else if ( playertxid != zeroid && games_playerdataspend(mtx,playertxid,vout,origplayergame) < 0 )
- return(cclib_error(result,"couldnt find playerdata to spend"));
- else if ( buyin > 0 && AddNormalinputs(mtx,mypk,buyin,64) < buyin )
- return(cclib_error(result,"couldnt find enough normal funds for buyin"));
- if ( tokenid != zeroid )
- {
- mtx.vin.push_back(CTxIn(tokenid,0)); // spending cc marker as token is burned
- char unspendableTokenAddr[64]; uint8_t tokenpriv[32]; struct CCcontract_info *cpTokens, tokensC;
- cpTokens = CCinit(&tokensC, EVAL_TOKENS);
- CPubKey unspPk = GetUnspendable(cpTokens, tokenpriv);
- GetCCaddress(cpTokens, unspendableTokenAddr, unspPk);
- CCaddr2set(cp, EVAL_TOKENS, unspPk, tokenpriv, unspendableTokenAddr);
- }
- mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,buyin + inputsum - txfee,gamespk,mypk));
- GetCCaddress1of2(cp,destaddr,gamespk,gamespk);
- CCaddr1of2set(cp,gamespk,gamespk,cp->CCpriv,destaddr);
- mtx.vout.push_back(MakeTokensCC1vout(cp->evalcode, 1, burnpk));
-
- uint8_t e, funcid; uint256 tid; std::vector voutPubkeys, voutPubkeysEmpty; int32_t didtx = 0;
- CScript opretRegister = games_registeropret(gametxid, playertxid);
- if ( playertxid != zeroid )
- {
- voutPubkeysEmpty.push_back(burnpk);
- if ( myGetTransaction(playertxid,playertx,hashBlock) != 0 )
- {
- std::vector> oprets;
- if ( (funcid= DecodeTokenOpRet(playertx.vout.back().scriptPubKey, e, tid, voutPubkeys, oprets)) != 0)
- { // if token in the opret
- didtx = 1;
- if ( funcid == 'c' )
- tid = tokenid == zeroid ? playertxid : tokenid;
- vscript_t vopretRegister;
- GetOpReturnData(opretRegister, vopretRegister);
- rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee,
- EncodeTokenOpRet(tid, voutPubkeysEmpty /*=never spent*/, std::make_pair(OPRETID_ROGUEGAMEDATA, vopretRegister)));
- }
- }
- }
- if ( didtx == 0 )
- rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, opretRegister);
-
- return(games_rawtxresult(result,rawtx,1));
- } else return(cclib_error(result,"invalid gametxid"));
- } else return(cclib_error(result,"no gametxid"));
- } else return(cclib_error(result,"couldnt reparse params"));
-}
-
-UniValue games_keystrokes(uint64_t txfee,struct CCcontract_info *cp,cJSON *params)
-{
- // vin0 -> baton from registration or previous keystrokes
- // vout0 -> new baton
- // opret -> user input chars
- // being killed should auto broadcast (possible to be suppressed?)
- // respawn to be prevented by including timestamps
- int32_t nextheight = hush_nextheight();
- CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight);
- UniValue result(UniValue::VOBJ); CPubKey gamespk,mypk; uint256 gametxid,playertxid,batontxid; int64_t batonvalue,buyin; std::vector keystrokes,playerdata; int32_t numplayers,regslot,numkeys,batonht,batonvout,n,elapsed,gameheight,maxplayers; CTransaction tx; CTxOut txout; char *keystrokestr,destaddr[64]; std::string rawtx,symbol,pname; bits256 t; uint8_t mypriv[32];
- // if ( txfee == 0 )
- txfee = 1000; // smaller than normal on purpose
- games_univalue(result,"keystrokes",-1,-1);
- if ( params != 0 && (n= cJSON_GetArraySize(params)) == 2 && (keystrokestr= jstr(jitem(params,1),0)) != 0 )
- {
- gametxid = juint256(jitem(params,0));
- result.push_back(Pair("gametxid",gametxid.GetHex()));
- result.push_back(Pair("keystrokes",keystrokestr));
- keystrokes = ParseHex(keystrokestr);
- mypk = pubkey2pk(Mypubkey());
- gamespk = GetUnspendable(cp,0);
- GetCCaddress1of2(cp,destaddr,gamespk,mypk);
- if ( games_isvalidgame(cp,gameheight,tx,buyin,maxplayers,gametxid,1) == 0 )
- {
- if ( games_findbaton(cp,playertxid,0,numkeys,regslot,playerdata,batontxid,batonvout,batonvalue,batonht,gametxid,tx,maxplayers,destaddr,numplayers,symbol,pname) == 0 )
- {
- result.push_back(Pair("batontxid",batontxid.GetHex()));
- result.push_back(Pair("playertxid",playertxid.GetHex()));
- if ( maxplayers == 1 || nextheight <= batonht+GAMES_MAXKEYSTROKESGAP )
- {
- mtx.vin.push_back(CTxIn(batontxid,batonvout,CScript())); //this validates user if pk
- mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,batonvalue-txfee,gamespk,mypk));
- Myprivkey(mypriv);
- CCaddr1of2set(cp,gamespk,mypk,mypriv,destaddr);
- rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,games_keystrokesopret(gametxid,batontxid,mypk,keystrokes));
- //fprintf(stderr,"KEYSTROKES.(%s)\n",rawtx.c_str());
- memset(mypriv,0,sizeof(mypriv));
- return(games_rawtxresult(result,rawtx,1));
- } else return(cclib_error(result,"keystrokes tx was too late"));
- } else return(cclib_error(result,"couldnt find batontxid"));
- } else return(cclib_error(result,"invalid gametxid"));
- } else return(cclib_error(result,"couldnt reparse params"));
-}
-
-gamesevent *games_extractgame(int32_t makefiles,char *str,int32_t *numkeysp,std::vector &newdata,uint64_t &seed,uint256 &playertxid,struct CCcontract_info *cp,uint256 gametxid,char *gamesaddr)
-{
- CPubKey gamespk; int32_t i,num,retval,maxplayers,gameheight,batonht,batonvout,numplayers,regslot,numkeys,err; std::string symbol,pname; CTransaction gametx; int64_t buyin,batonvalue; char fname[64]; gamesevent *keystrokes = 0; std::vector playerdata; uint256 batontxid; FILE *fp; uint8_t newplayer[10000]; struct games_player P,endP;
- gamespk = GetUnspendable(cp,0);
- *numkeysp = 0;
- seed = 0;
- num = numkeys = 0;
- playertxid = zeroid;
- str[0] = 0;
- if ( (err= games_isvalidgame(cp,gameheight,gametx,buyin,maxplayers,gametxid,0)) == 0 )
- {
- if ( (retval= games_findbaton(cp,playertxid,&keystrokes,numkeys,regslot,playerdata,batontxid,batonvout,batonvalue,batonht,gametxid,gametx,maxplayers,gamesaddr,numplayers,symbol,pname)) == 0 )
- {
- UniValue obj;
- //fprintf(stderr,"got baton\n");
- seed = games_gamefields(obj,maxplayers,buyin,gametxid,gamesaddr);
- //fprintf(stderr,"(%s) found baton %s numkeys.%d seed.%llu playerdata.%d playertxid.%s\n",pname.size()!=0?pname.c_str():Games_pname.c_str(),batontxid.ToString().c_str(),numkeys,(long long)seed,(int32_t)playerdata.size(),playertxid.GetHex().c_str());
- memset(&P,0,sizeof(P));
- if ( playerdata.size() > 0 )
- {
- for (i=0; i no playerdata\n");
- newdata.resize(0);
- *numkeysp = numkeys;
- return(keystrokes);
- }
- else
- {
- *numkeysp = numkeys;
- return(keystrokes);
- }
- } else num = 0;
- }
- else
- {
- fprintf(stderr,"extractgame: couldnt find baton keystrokes.%p retval.%d\n",keystrokes,retval);
- if ( keystrokes != 0 )
- free(keystrokes), keystrokes = 0;
- }
- } else fprintf(stderr,"extractgame: invalid game\n");
- //fprintf(stderr,"extract %s\n",gametxid.GetHex().c_str());
- return(0);
-}
-
-UniValue games_extract(uint64_t txfee,struct CCcontract_info *cp,cJSON *params)
-{
- UniValue result(UniValue::VOBJ); CPubKey pk,gamespk; int32_t i,n,numkeys,flag = 0; uint64_t seed; char str[512],gamesaddr[64],*pubstr,*hexstr; gamesevent *keystrokes = 0; std::vector newdata; uint256 gametxid,playertxid; FILE *fp; uint8_t pub33[33];
- pk = pubkey2pk(Mypubkey());
- gamespk = GetUnspendable(cp,0);
- result.push_back(Pair("name","games"));
- result.push_back(Pair("method","extract"));
- gamesaddr[0] = 0;
- if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 )
- {
- if ( n > 0 )
- {
- gametxid = juint256(jitem(params,0));
- result.push_back(Pair("gametxid",gametxid.GetHex()));
- if ( n == 2 )
- {
- if ( (pubstr= jstr(jitem(params,1),0)) != 0 )
- {
- if (strlen(pubstr) == 66 )
- {
- decode_hex(pub33,33,pubstr);
- pk = buf2pk(pub33);
- }
- else if ( strlen(pubstr) < 36 )
- strcpy(gamesaddr,pubstr);
- }
- //fprintf(stderr,"gametxid.%s %s\n",gametxid.GetHex().c_str(),pubstr);
- }
- if ( gamesaddr[0] == 0 )
- GetCCaddress1of2(cp,gamesaddr,gamespk,pk);
- result.push_back(Pair("gamesaddr",gamesaddr));
- str[0] = 0;
- if ( (keystrokes= games_extractgame(1,str,&numkeys,newdata,seed,playertxid,cp,gametxid,gamesaddr)) != 0 )
- {
- result.push_back(Pair("status","success"));
- flag = 1;
- hexstr = (char *)calloc(1,sizeof(gamesevent)*numkeys*2 + 1);
- for (i=0; i highlander vout from creategame TCBOO
- //vin1 -> keystrokes baton of completed game, must be last to quit or first to win, only spent registration batons matter. If more than 60 blocks since last keystrokes, it is forfeit
- //vins2+ -> rest of unspent registration utxo so all newgame vouts are spent
- //vout0 -> nonfungible character with pack @
- //vout1 -> 1% ingame gold and all the buyins
-
- // detect if last to bailout
- // vin0 -> kestrokes baton of completed game with Q
- // vout0 -> playerdata marker
- // vout0 -> 1% ingame gold
- // get any playerdata, get all keystrokes, replay game and compare final state
- CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
- UniValue result(UniValue::VOBJ); std::string rawtx,symbol,pname; CTransaction gametx; uint64_t seed; int64_t buyin,batonvalue,inputsum,cashout=0,CCchange=0; int32_t i,err,gameheight,tmp,numplayers,regslot,n,num,numkeys,maxplayers,batonht,batonvout; char mygamesaddr[64],str[512]; gamesevent *keystrokes = 0; std::vector playerdata,newdata,nodata; uint256 batontxid,playertxid,gametxid; CPubKey mypk,gamespk; uint8_t player[10000],mypriv[32],funcid;
- struct CCcontract_info *cpTokens, tokensC;
-
- if ( txfee == 0 )
- txfee = 10000;
- mypk = pubkey2pk(Mypubkey());
- gamespk = GetUnspendable(cp,0);
- GetCCaddress1of2(cp,mygamesaddr,gamespk,mypk);
- result.push_back(Pair("name","games"));
- result.push_back(Pair("method",method));
- result.push_back(Pair("mygamesaddr",mygamesaddr));
- if ( strcmp(method,"bailout") == 0 )
- funcid = 'Q';
- else funcid = 'H';
- if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 )
- {
- if ( n > 0 )
- {
- gametxid = juint256(jitem(params,0));
- result.push_back(Pair("gametxid",gametxid.GetHex()));
- if ( (err= games_isvalidgame(cp,gameheight,gametx,buyin,maxplayers,gametxid,1)) == 0 )
- {
- if ( games_findbaton(cp,playertxid,&keystrokes,numkeys,regslot,playerdata,batontxid,batonvout,batonvalue,batonht,gametxid,gametx,maxplayers,mygamesaddr,numplayers,symbol,pname) == 0 )
- {
- UniValue obj; struct games_player P;
- seed = games_gamefields(obj,maxplayers,buyin,gametxid,mygamesaddr);
- fprintf(stderr,"(%s) found baton %s numkeys.%d seed.%llu playerdata.%d\n",pname.size()!=0?pname.c_str():Games_pname.c_str(),batontxid.ToString().c_str(),numkeys,(long long)seed,(int32_t)playerdata.size());
- memset(&P,0,sizeof(P));
- if ( playerdata.size() > 0 )
- {
- for (i=0; i 0 )
- {
- newdata.resize(num);
- for (i=0; i no playerdata\n");
- newdata.resize(0);
- }
- else
- {
- cpTokens = CCinit(&tokensC, EVAL_TOKENS);
- mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, txfee, GetUnspendable(cpTokens,NULL))); // marker to token cc addr, burnable and validated
- mtx.vout.push_back(MakeTokensCC1vout(cp->evalcode,1,mypk));
- cashout = games_cashout(&P);
- fprintf(stderr,"\ncashout %.8f extracted %s\n",(double)cashout/COIN,str);
- if ( funcid == 'H' && maxplayers > 1 )
- {
- /*if ( P.amulet == 0 )
- {
- if ( numplayers != maxplayers )
- return(cclib_error(result,"numplayers != maxplayers"));
- else if ( games_playersalive(tmp,tmp,gametxid,maxplayers,gameheight,gametx) > 1 )
- return(cclib_error(result,"highlander must be a winner or last one standing"));
- }*/
- cashout += games_buyins(gametxid,maxplayers);//numplayers * buyin;
- }
- if ( cashout > 0 )
- {
- if ( (inputsum= AddCClibInputs(cp,mtx,gamespk,cashout,60,cp->unspendableCCaddr,1)) > cashout )
- CCchange = (inputsum - cashout);
- else fprintf(stderr,"couldnt find enough utxos\n");
- }
- mtx.vout.push_back(CTxOut(cashout,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
- }
- }
- if ( CCchange + (batonvalue-3*txfee) >= txfee )
- mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange + (batonvalue-3*txfee),gamespk));
- Myprivkey(mypriv);
- CCaddr1of2set(cp,gamespk,mypk,mypriv,mygamesaddr);
- CScript opret;
- if ( pname.size() == 0 )
- pname = Games_pname;
- if ( newdata.size() == 0 )
- {
- opret = games_finishopret(funcid, gametxid, regslot, mypk, nodata,pname);
- rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,opret);
- //fprintf(stderr,"nodata finalizetx.(%s)\n",rawtx.c_str());
- }
- else
- {
- opret = games_finishopret(funcid, gametxid, regslot, mypk, newdata,pname);
- char seedstr[32];
- sprintf(seedstr,"%llu",(long long)seed);
- std::vector vopretNonfungible;
- GetOpReturnData(opret, vopretNonfungible);
- rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, EncodeTokenCreateOpRet('c', Mypubkey(), std::string(seedstr), gametxid.GetHex(), vopretNonfungible));
- }
- memset(mypriv,0,sizeof(mypriv));
- return(games_rawtxresult(result,rawtx,1));
- }
- result.push_back(Pair("result","success"));
- } else fprintf(stderr,"illegal game err.%d\n",err);
- } else fprintf(stderr,"parameters only n.%d\n",n);
- }
- return(result);
-}
-
-UniValue games_bailout(uint64_t txfee,struct CCcontract_info *cp,cJSON *params)
-{
- return(games_finish(txfee,cp,params,(char *)"bailout"));
-}
-
-UniValue games_highlander(uint64_t txfee,struct CCcontract_info *cp,cJSON *params)
-{
- return(games_finish(txfee,cp,params,(char *)"highlander"));
-}
-
-UniValue games_players(uint64_t txfee,struct CCcontract_info *cp,cJSON *params)
-{
- UniValue result(UniValue::VOBJ),a(UniValue::VARR); int64_t buyin; uint256 tokenid,gametxid,txid,hashBlock; CTransaction playertx,tx; int32_t maxplayers,vout,numvouts; std::vector playerdata; CPubKey gamespk,mypk,pk; std::string symbol,pname; char coinaddr[64];
- std::vector > unspentOutputs;
- gamespk = GetUnspendable(cp,0);
- mypk = pubkey2pk(Mypubkey());
- GetTokensCCaddress(cp,coinaddr,mypk);
- SetCCunspents(unspentOutputs,coinaddr,true);
- games_univalue(result,"players",-1,-1);
- for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
- {
- txid = it->first.txhash;
- vout = (int32_t)it->first.index;
- //char str[65]; fprintf(stderr,"%s check %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN);
- if ( it->second.satoshis != 1 || vout > 1 )
- continue;
- if ( games_playerdata(cp,gametxid,tokenid,pk,playerdata,symbol,pname,txid) == 0 )//&& pk == mypk )
- {
- a.push_back(txid.GetHex());
- //a.push_back(Pair("playerdata",games_playerobj(playerdata)));
- }
- }
- result.push_back(Pair("playerdata",a));
- result.push_back(Pair("numplayerdata",(int64_t)a.size()));
- return(result);
-}
-
-UniValue games_games(uint64_t txfee,struct CCcontract_info *cp,cJSON *params)
-{
- UniValue result(UniValue::VOBJ),a(UniValue::VARR),b(UniValue::VARR); uint256 txid,hashBlock,gametxid,tokenid,playertxid; int32_t vout,maxplayers,gameheight,numvouts; CPubKey gamespk,mypk; char coinaddr[64]; CTransaction tx,gametx; int64_t buyin;
- std::vector txids;
- gamespk = GetUnspendable(cp,0);
- mypk = pubkey2pk(Mypubkey());
- GetCCaddress1of2(cp,coinaddr,gamespk,mypk);
- SetCCtxids(txids,coinaddr,true,cp->evalcode,zeroid,'R');
- games_univalue(result,"games",-1,-1);
- for (std::vector::const_iterator it=txids.begin(); it!=txids.end(); it++)
- {
- txid = *it;
- //char str[65]; fprintf(stderr,"%s check %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN);
- if ( myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 1 )
- {
- if ( games_registeropretdecode(gametxid,tokenid,playertxid,tx.vout[numvouts-1].scriptPubKey) == 'R' )
- {
- if ( games_isvalidgame(cp,gameheight,gametx,buyin,maxplayers,gametxid,0) == 0 )
- {
- if ( CCgettxout(txid,vout,1,0) < 0 )
- b.push_back(gametxid.GetHex());
- else a.push_back(gametxid.GetHex());
- }
- }
- }
- }
- result.push_back(Pair("pastgames",b));
- result.push_back(Pair("games",a));
- result.push_back(Pair("numgames",(int64_t)(a.size()+b.size())));
- return(result);
-}
-
-UniValue games_setname(uint64_t txfee,struct CCcontract_info *cp,cJSON *params)
-{
- UniValue result(UniValue::VOBJ); int32_t n; char *namestr = 0;
- games_univalue(result,"setname",-1,-1);
- if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 )
- {
- if ( n > 0 )
- {
- if ( (namestr= jstri(params,0)) != 0 )
- {
- result.push_back(Pair("result","success"));
- result.push_back(Pair("pname",namestr));
- Games_pname = namestr;
- return(result);
- }
- }
- }
- result.push_back(Pair("result","error"));
- result.push_back(Pair("error","couldnt get name"));
- return(result);
-}
-
-UniValue games_fund(uint64_t txfee,struct CCcontract_info *cp,cJSON *params)
-{
- CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
- UniValue result(UniValue::VOBJ); std::string rawtx; int64_t amount,inputsum; CPubKey gamespk,mypk; CScript opret;
- if ( params != 0 && cJSON_GetArraySize(params) == 1 )
- {
- amount = jdouble(jitem(params,0),0) * COIN + 0.0000000049;
- gamespk = GetUnspendable(cp,0);
- mypk = pubkey2pk(Mypubkey());
- if ( amount > GAMES_TXFEE )
- {
- if ( (inputsum= AddNormalinputs(mtx,mypk,amount+GAMES_TXFEE,64)) >= amount+GAMES_TXFEE )
- {
- mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,gamespk));
- rawtx = FinalizeCCTx(0,cp,mtx,mypk,GAMES_TXFEE,opret);
- return(games_rawtxresult(result,rawtx,1));
- }
- else
- {
- result.push_back(Pair("result","error"));
- result.push_back(Pair("error","not enough funds"));
- }
- }
- else
- {
- result.push_back(Pair("result","error"));
- result.push_back(Pair("error","amount too small"));
- }
- }
- else
- {
- result.push_back(Pair("result","error"));
- result.push_back(Pair("error","couldnt parse"));
- }
- return(result);
-}
-
-int32_t games_playerdata_validate(int64_t *cashoutp,uint256 &playertxid,struct CCcontract_info *cp,std::vector playerdata,uint256 gametxid,CPubKey pk)
-{
- static uint32_t good,bad; static uint256 prevgame;
- char str[512],gamesaddr[64],str2[67],fname[64]; gamesevent *keystrokes; int32_t i,numkeys; std::vector newdata; uint64_t seed; CPubKey gamespk; struct games_player P;
- *cashoutp = 0;
- gamespk = GetUnspendable(cp,0);
- GetCCaddress1of2(cp,gamesaddr,gamespk,pk);
- if ( (keystrokes= games_extractgame(0,str,&numkeys,newdata,seed,playertxid,cp,gametxid,gamesaddr)) != 0 )
- {
- free(keystrokes);
- sprintf(fname,"%s.%llu.pack",GAMENAME,(long long)seed);
- remove(fname);
- for (i=0; i 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 <-> HUSH gateway deposit address
- HUSH 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. HUSH 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 HUSH 1000000 HUSH_equivalent_token_for_gatewaysCC
- a7398a8748354dd0a3f8d07d70e65294928ecc3674674bb2d9483011ccaa9a7a
-
- transfer to gateways pubkey: 03ea9c062b9652d8eff34879b504eda0717895d27597aaeb60347d65eed96ccb40 RDMqGyREkP1Gwub1Nr5Ye8a325LGZsWBCb
- ./c tokentransfer a7398a8748354dd0a3f8d07d70e65294928ecc3674674bb2d9483011ccaa9a7a 03ea9c062b9652d8eff34879b504eda0717895d27597aaeb60347d65eed96ccb40 100000000000000
- 2206fc39c0f384ca79819eb491ddbf889642cbfe4d0796bb6a8010ed53064a56
-
- ./c oraclescreate HUSH blockheaders Ihh
- 1f1aefcca2bdea8196cfd77337fb21de22d200ddea977c2f9e8742c55829d808
-
- ./c oraclesregister 1f1aefcca2bdea8196cfd77337fb21de22d200ddea977c2f9e8742c55829d808 1000000
- 83b59eac238cbe54616ee13b2fdde85a48ec869295eb04051671a1727c9eb402
-
- ./c oraclessubscribe 1f1aefcca2bdea8196cfd77337fb21de22d200ddea977c2f9e8742c55829d808 02ebc786cb83de8dc3922ab83c21f3f8a2f3216940c3bf9da43ce39e2a3a882c92 1000
- f9499d8bb04ffb511fcec4838d72e642ec832558824a2ce5aed87f1f686f8102
-
- gatewaysbind tokenid oracletxid coin tokensupply M N pubkey(s)
- ./c gatewaysbind a7398a8748354dd0a3f8d07d70e65294928ecc3674674bb2d9483011ccaa9a7a 1f1aefcca2bdea8196cfd77337fb21de22d200ddea977c2f9e8742c55829d808 HUSH 100000000000000 1 1 02ebc786cb83de8dc3922ab83c21f3f8a2f3216940c3bf9da43ce39e2a3a882c92
- e6c99f79d4afb216aa8063658b4222edb773dd24bb0f8e91bd4ef341f3e47e5e
-
- ./c gatewaysinfo e6c99f79d4afb216aa8063658b4222edb773dd24bb0f8e91bd4ef341f3e47e5e
- {
- "result": "success",
- "name": "Gateways",
- "pubkey": "02ebc786cb83de8dc3922ab83c21f3f8a2f3216940c3bf9da43ce39e2a3a882c92",
- "coin": "HUSH",
- "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 HUSH to appear in.
-
- 0223d114dededb04f253816d6ad0ce78dd08c617c94ce3c53bf50dc74a5157bef8 pubkey for RFpxgqff7FDHFuHa3jSX5NzqqWCcELz8ha
- ./hush-cli z_sendmany "" '[{"address":"RFpxgqff7FDHFuHa3jSX5NzqqWCcELz8ha","amount":0.0001},{"address":"RHV2As4rox97BuE3LK96vMeNY8VsGRTmBj","amount":7.6999}]'
- bc41a00e429db741c3199f17546a48012fd3b7eea45dfc6bc2f5228278133009 height.1003776 merkle.90aedc2f19200afc9aca2e351438d011ebae8264a58469bf225883045f61917f
-
- ./hush-cli gettxoutproof '["bc41a00e429db741c3199f17546a48012fd3b7eea45dfc6bc2f5228278133009"]'
- 04000000232524ea04b54489eb222f8b3f566ed35504e3050488a63f0ab7b1c2030000007f91615f04835822bf6984a56482aeeb11d03814352eca9afc0a20192fdcae900000000000000000000000000000000000000000000000000000000000000000268e965ba608071d0800038e16762900000000000000000000000000000000000000000042008dd6fd4005016b4292b05df6dfd316c458c53e28fb2befb96e4870a40c6c04e733d75d8a7a18cce34fe326005efdc403bfa4644e30eeafdaeff34419edc591299e6cc5933cb2eeecbab5c4dfe97cd413b75215999a3dd02b540373581e81d8512bff1640590a6b4da4aaa9b8adc0102c38ca0022daed997b53ed192ba326e212fba5e505ce29e3ad149cef7f48d0e00948a1acd81731d84008760759211eb4abbc7b037939a7964182edb59cf9065357e864188ee5fc7316e8796963036bb99eeb9f06c95d64f78749ecec7181c12eb5d83a3b9b1c1e8a0aae9a20ce04a250b28216620bfc99bb81a6de4db80b93a5aea916de97c1a272e26644abdd683f19c5e3174a2e4513ed767d8f11a4c3074295f697839c5d9139676a813451cc7da38f68cbae5d990a79075f98903233ca04fe1b4b099e433585e5adcc45d41d54a9c648179297359c75950a5e574f13f70b728bbbf552770256315cd0a00139d6ab6934cb5ed70a4fc01a92611b096dd0028f17f4cc687b75f37dca530aa47a18321c50528dbd9272eabb3e13a87021a05918a6d2627e2caba6d7cf1a9f0b831ea3337b9a6af92746d83140078d60c72b6beacf91c9e68a34cee209e08670be1d17ff8d80b7a2285b1325461a2e33f2ee675593f1900e066a5d212615cd8da18749b0e684eee73edcc9031709be715b889c6d015cf4bd4ad5ab7e21bd3492c208930a54d353ef36a437f507ead38855633c1b88d060d9e4221ca8ce2f698e8a6ae0d41e9ace3cbd401f1e0f07650e9c126d4ef20278c8be6e85c7637513643f8d02d7ad64c09da11c16429d60e5160c345844b8158ece62794e8ad280d4e4664150e74978609ece431e51a9f9e1ce8aa49c16f36c7fd12b71acc42d893e18476b8b1e144a8175519612efc93e0aecc61f3b21212c958b0e2331d76aaa62faf11a58fe2bd91ab9ab01b906406c9bbc02df2a106e67182aae0a20b538bf19f09c57f9de5e198ba254580fb1b11e22ad526550093420cb7c68628d4c3ad329c8acc6e219093d277810ed016b6099b7e3781de412a22dacedaa2acf29e8062debcd85c7b9529a20b2782a2470763ac27cf89611a527d43ac89b8063ffb93b6ed993425194f8ee821a8493a563072c896f9584f95db28e3f2fc5fb4a6f3c39d615cd563641717cd50afb73ed3989cbf504b2043882993ce9575f56402534173b1396fbc13df80920b46788ae340ad5a91f25177cc74aa69024d76f56166199d2e4d50a053555256c4e3137ea1cee1130e916a88b6ee5cf2c85652fb8824d5dacfa485e3ef6190591ac0c2fcacc4fc7deb65aca4b0b89b76e35a46b0627e2e967cc63a5d606a984c8e63eabb98fde3e69114340ae524c974cb936e57690e98a7a74533f6f7d1d0496976496b54d14a8163efb32b70dfbb79d80a3022c4f53571c08bf044270565716b435084376714b224ab23e9817c05af8223723afc0577af5c8fc28f71036ca82528aaa4ca9bcd18a50e25d2a528f183d3a2074d968d170876d8dce434c5937261b55173ab87e03d5632ca0834fdc5387c15ab3a17d75c0f274004f289ff1bf7d14e97fdf4172eb49adfb418cc2f4794806ae7c0111c97df4d65d38679ec93fea3ef738ed565e8906a8fe1861cafe3938c772fedcfab40159938e06ef414fd299f2355c6d3369bc1bd3c4db64ce205f0a1b70a40030f505b736e28230de82e97776b5ee7b10708bb3020d28cec7a8e124549ec80c547ac4e7b52bf397c72bcfce30820554ab8fb4d1f73b209bc32a0e7e878843cdbf5f01222728ccea7e6ab7cb5e3fee3234f5b85d1985f91492f6ceaa6454a658dab5074f163ce26ed753137fa61c940679de13bd7b212cd3cf2b334f5201cecbc7473342bd7a239e09169bccd56d03000000037a9068df0625e548e71263c8361b4e904c998378f6b9e32729c3f19b10ad752e093013788222f5c26bfc5da4eeb7d32f01486a54179f19c341b79d420ea041bc8878d22fad4692b2d609c3cf190903874d3682a714c7483518c9392e07c25035010b
-
- ./hush-cli getrawtransaction bc41a00e429db741c3199f17546a48012fd3b7eea45dfc6bc2f5228278133009
- 010000000149964cdcd17fe9b1cae4d0f3b5f5db301d9b4f54099fdf4d34498df281757094010000006a4730440220594f3a630dd73c123f44621aa8bb9968ab86734833453dd479af6d79ae6f584202207bb5e35f13b337ccc8a88d9a006c8c5ddb016c0a6f4f2dc44357a8128623d85d01210223154bf53cd3a75e64d86697070d6437c8f0010a09c1df35b659e31ce3d79b5dffffffff0310270000000000001976a91447d2e323a14b0c3be08698aa46a9b91489b189d688ac701de52d000000001976a91459fdba29ea85c65ad90f6d38f7a6646476b26b1688acb0a86a00000000001976a914f9a9daf5519dae38b8b61d945f075da895df441d88ace18d965b
-
- gatewaysdeposit bindtxid height coin cointxid claimvout deposithex proof destpub amount
- ./hush-cli -ac_name=AT5 gatewaysdeposit e6c99f79d4afb216aa8063658b4222edb773dd24bb0f8e91bd4ef341f3e47e5e 1003776 HUSH 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 HUSH 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 HUSH 03b7621b44118017a16043f19b30cc8a4cfe068ac4e42417bae16ba460c80f3828 1
- ef3cc452da006eb2edda6b6ed3d3347664be51260f3e91f59ec44ec9701367f0
-
- Now there is a withdraw pending, so it needs to be processed by the signing nodes on the HUSH 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 HUSH
-
-
- Implementation Issues:
- -- When thinking about validation, it is clear that we cant use EVAL_ASSETS for the locked coins as there wont be any enforcement of the gateways locking.
- -- This means we need a way to transfer assets into gateways outputs and back. It seems a tokenconvert rpc will be needed and hopefully that will be enough to make it all work properly.
- ++ The use of tokenconvert has been changed to the use of the new Tokens contract which can enforce other contracts validation by forwarding eval->validate call to GatewaysValidate
- ++ So all tokens remain within that Tokens contract eval code.
-
- -- Care must be taken so that tokens are not lost and can be converted back.
- -- This changes the usage to require tokenconvert before doing the bind and also tokenconvert before doing a withdraw. EVAL_GATEWAYS has evalcode of 241
- ++ tokenconvert now returns 'not implemented', no need to use it at all.
-
- -- The gatewaysclaim automatically converts the deposit amount of tokens back to EVAL_ASSETS.
- ++ The gatewaysclaim automatically transfers the deposit amount of tokens to depositor's address (within EVAL_TOKENS).
-
- */
-// start of consensus code
-
-#define HUSH_PUBTYPE 60
-#define HUSH_P2SHTYPE 85
-#define HUSH_WIFTYPE 188
-#define HUSH_TADDR 0
-#define CC_MARKER_VALUE 10000
-
-CScript EncodeGatewaysBindOpRet(uint8_t funcid,uint256 tokenid,std::string coin,int64_t totalsupply,uint256 oracletxid,uint8_t M,uint8_t N,std::vector gatewaypubkeys,uint8_t taddr,uint8_t prefix,uint8_t prefix2,uint8_t wiftype)
-{
- CScript opret; uint8_t evalcode = EVAL_GATEWAYS; struct CCcontract_info *cp,C; CPubKey gatewayspk;
- std::vector pubkeys;
- vscript_t vopret;
-
- cp = CCinit(&C,EVAL_GATEWAYS);
- gatewayspk = GetUnspendable(cp,0);
- pubkeys.push_back(gatewayspk);
- vopret = E_MARSHAL(ss << evalcode << funcid << coin << totalsupply << oracletxid << M << N << gatewaypubkeys << taddr << prefix << prefix2 << wiftype);
- return(EncodeTokenOpRet(tokenid,pubkeys, std::make_pair(OPRETID_GATEWAYSDATA, vopret)));
-}
-
-uint8_t DecodeGatewaysBindOpRet(char *depositaddr,const CScript &scriptPubKey,uint256 &tokenid,std::string &coin,int64_t &totalsupply,uint256 &oracletxid,uint8_t &M,uint8_t &N,std::vector &gatewaypubkeys,uint8_t &taddr,uint8_t &prefix,uint8_t &prefix2,uint8_t &wiftype)
-{
- std::vector> oprets;
- std::vector vopret,vOpretExtra; uint8_t *script,e,f,tokenevalcode; std::vector pubkeys;
-
- if (DecodeTokenOpRet(scriptPubKey,tokenevalcode,tokenid,pubkeys,oprets)!=0 && GetOpretBlob(oprets, OPRETID_GATEWAYSDATA, vOpretExtra) && tokenevalcode==EVAL_TOKENS && vOpretExtra.size()>0)
- {
- vopret=vOpretExtra;
- }
- else 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 >> totalsupply; ss >> oracletxid; ss >> M; ss >> N; ss >> gatewaypubkeys; ss >> taddr; ss >> prefix; ss >> prefix2; ss >> wiftype) != 0 )
- {
- if ( prefix == HUSH_PUBTYPE && prefix2 == HUSH_P2SHTYPE )
- {
- if ( N > 1 )
- {
- strcpy(depositaddr,CBitcoinAddress(CScriptID(GetScriptForMultisig(M,gatewaypubkeys))).ToString().c_str());
- LOGSTREAM("gatewayscc", CCLOG_DEBUG1, stream << "f." << f << " M." << (int)M << " of N." << (int)N << " size." << (int32_t)gatewaypubkeys.size() << " -> " << depositaddr << std::endl);
- } else Getscriptaddress(depositaddr,CScript() << ParseHex(HexStr(gatewaypubkeys[0])) << OP_CHECKSIG);
- }
- else
- {
- if ( N > 1 ) strcpy(depositaddr,CCustomBitcoinAddress(CScriptID(GetScriptForMultisig(M,gatewaypubkeys)),taddr,prefix,prefix2).ToString().c_str());
- else GetCustomscriptaddress(depositaddr,CScript() << ParseHex(HexStr(gatewaypubkeys[0])) << OP_CHECKSIG,taddr,prefix,prefix2);
- }
- return(f);
- } else LOGSTREAM("gatewayscc",CCLOG_DEBUG1, stream << "error decoding bind opret" << std::endl);
- return(0);
-}
-
-CScript EncodeGatewaysDepositOpRet(uint8_t funcid,uint256 bindtxid,std::string refcoin,std::vector publishers,std::vectortxids,int32_t height,uint256 cointxid,int32_t claimvout,std::string deposithex,std::vectorproof,CPubKey destpub,int64_t amount)
-{
- CScript opret; uint8_t evalcode = EVAL_GATEWAYS;
-
- opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << refcoin << bindtxid << publishers << txids << height << cointxid << claimvout << deposithex << proof << destpub << amount);
- return(opret);
-}
-
-uint8_t DecodeGatewaysDepositOpRet(const CScript &scriptPubKey,uint256 &bindtxid,std::string &refcoin,std::vector&publishers,std::vector&txids,int32_t &height,uint256 &cointxid, int32_t &claimvout,std::string &deposithex,std::vector &proof,CPubKey &destpub,int64_t &amount)
-{
- std::vector 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 >> refcoin; ss >> bindtxid; ss >> publishers; ss >> txids; ss >> height; ss >> cointxid; ss >> claimvout; ss >> deposithex; ss >> proof; ss >> destpub; ss >> amount) != 0 )
- {
- return(f);
- }
- return(0);
-}
-
-CScript EncodeGatewaysClaimOpRet(uint8_t funcid,uint256 tokenid,uint256 bindtxid,std::string refcoin,uint256 deposittxid,CPubKey destpub,int64_t amount)
-{
- CScript opret; uint8_t evalcode = EVAL_GATEWAYS; struct CCcontract_info *cp,C; CPubKey gatewayspk;
- std::vector pubkeys;
- vscript_t vopret;
-
- pubkeys.push_back(destpub);
- vopret = /*<< OP_RETURN <<*/ E_MARSHAL(ss << evalcode << funcid << bindtxid << refcoin << deposittxid << destpub << amount);
- return(EncodeTokenOpRet(tokenid,pubkeys, make_pair(OPRETID_GATEWAYSDATA, vopret)));
-}
-
-uint8_t DecodeGatewaysClaimOpRet(const CScript &scriptPubKey,uint256 &tokenid,uint256 &bindtxid,std::string &refcoin,uint256 &deposittxid,CPubKey &destpub,int64_t &amount)
-{
- std::vector> oprets;
- std::vector vopret,vOpretExtra; uint8_t *script,e,f,tokenevalcode; std::vector pubkeys;
-
- if (DecodeTokenOpRet(scriptPubKey,tokenevalcode,tokenid,pubkeys, oprets)!=0 && GetOpretBlob(oprets, OPRETID_GATEWAYSDATA, vOpretExtra) && tokenevalcode==EVAL_TOKENS && vOpretExtra.size()>0)
- {
- vopret=vOpretExtra;
- }
- else GetOpReturnData(scriptPubKey, vopret);
- script = (uint8_t *)vopret.data();
- if ( vopret.size() > 2 && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> bindtxid; ss >> refcoin; ss >> deposittxid; ss >> destpub; ss >> amount) != 0 )
- {
- return(f);
- }
- return(0);
-}
-
-CScript EncodeGatewaysWithdrawOpRet(uint8_t funcid,uint256 tokenid,uint256 bindtxid,std::string refcoin,CPubKey withdrawpub,int64_t amount)
-{
- CScript opret; uint8_t evalcode = EVAL_GATEWAYS; struct CCcontract_info *cp,C; CPubKey gatewayspk;
- std::vector pubkeys;
- vscript_t vopret;
-
- cp = CCinit(&C,EVAL_GATEWAYS);
- gatewayspk = GetUnspendable(cp,0);
- pubkeys.push_back(gatewayspk);
- vopret = /*opret << OP_RETURN << */ E_MARSHAL(ss << evalcode << funcid << bindtxid << refcoin << withdrawpub << amount);
- return(EncodeTokenOpRet(tokenid,pubkeys, std::make_pair(OPRETID_GATEWAYSDATA, vopret)));
-}
-
-uint8_t DecodeGatewaysWithdrawOpRet(const CScript &scriptPubKey, uint256& tokenid, uint256 &bindtxid, std::string &refcoin, CPubKey &withdrawpub, int64_t &amount)
-{
- std::vector> oprets;
- std::vector vopret,vOpretExtra; uint8_t *script,e,f,tokenevalcode; std::vector pubkeys;
-
- if (DecodeTokenOpRet(scriptPubKey,tokenevalcode,tokenid,pubkeys, oprets)!=0 && GetOpretBlob(oprets, OPRETID_GATEWAYSDATA, vOpretExtra) && tokenevalcode==EVAL_TOKENS && vOpretExtra.size()>0)
- {
- vopret=vOpretExtra;
- }
- else GetOpReturnData(scriptPubKey, vopret);
- script = (uint8_t *)vopret.data();
- if ( vopret.size() > 2 && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> bindtxid; ss >> refcoin; ss >> withdrawpub; ss >> amount) != 0 )
- {
- return(f);
- }
- return(0);
-}
-
-CScript EncodeGatewaysPartialOpRet(uint8_t funcid, uint256 withdrawtxid,std::string refcoin,uint8_t K, CPubKey signerpk,std::string hex)
-{
- CScript opret; uint8_t evalcode = EVAL_GATEWAYS;
-
- opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << withdrawtxid << refcoin << K << signerpk << hex);
- return(opret);
-}
-
-uint8_t DecodeGatewaysPartialOpRet(const CScript &scriptPubKey,uint256 &withdrawtxid,std::string &refcoin,uint8_t &K,CPubKey &signerpk,std::string &hex)
-{
- std::vector 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 >> withdrawtxid; ss >> refcoin; ss >> K; ss >> signerpk; ss >> hex) != 0 )
- {
- return(f);
- }
- return(0);
-}
-
-CScript EncodeGatewaysCompleteSigningOpRet(uint8_t funcid,uint256 withdrawtxid,std::string refcoin,uint8_t K,std::string hex)
-{
- CScript opret; uint8_t evalcode = EVAL_GATEWAYS;
- opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << withdrawtxid << refcoin << K << hex);
- return(opret);
-}
-
-uint8_t DecodeGatewaysCompleteSigningOpRet(const CScript &scriptPubKey,uint256 &withdrawtxid,std::string &refcoin,uint8_t &K,std::string &hex)
-{
- std::vector 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 >> withdrawtxid; ss >> refcoin; ss >> K; ss >> hex) != 0 )
- {
- return(f);
- }
- return(0);
-}
-
-CScript EncodeGatewaysMarkDoneOpRet(uint8_t funcid,uint256 withdrawtxid,std::string refcoin,uint256 completetxid)
-{
- CScript opret; uint8_t evalcode = EVAL_GATEWAYS;
-
- opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << withdrawtxid << refcoin << completetxid);
- return(opret);
-}
-
-uint8_t DecodeGatewaysMarkDoneOpRet(const CScript &scriptPubKey, uint256 &withdrawtxid, std::string &refcoin, uint256 &completetxid)
-{
- std::vector 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 >> withdrawtxid; ss >> refcoin; ss >> completetxid;) != 0 )
- {
- return(f);
- }
- return(0);
-}
-
-uint8_t DecodeGatewaysOpRet(const CScript &scriptPubKey)
-{
- std::vector> oprets;
- std::vector vopret,vOpretExtra; uint8_t *script,e,f,tokenevalcode; std::vector pubkeys; uint256 tokenid;
-
- if (DecodeTokenOpRet(scriptPubKey,tokenevalcode,tokenid,pubkeys, oprets)!=0 && GetOpretBlob(oprets, OPRETID_GATEWAYSDATA, vOpretExtra) && tokenevalcode==EVAL_TOKENS && vOpretExtra.size()>0)
- {
- vopret=vOpretExtra;
- }
- else GetOpReturnData(scriptPubKey, vopret);
- script = (uint8_t *)vopret.data();
- if ( vopret.size() > 2 && script[0] == EVAL_GATEWAYS)
- {
- f=script[1];
- if (f == 'B' || f == 'D' || f == 'C' || f == 'W' || f == 'P' || f == 'S' || f == 'M')
- return(f);
- }
- 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; iismyvin)(tx.vin[i].scriptSig) != 0 )
- {
- LOGSTREAM("gatewayscc",CCLOG_DEBUG2, stream << "vini." << i << " check mempool" << std::endl);
- if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 )
- return eval->Invalid("cant find vinTx");
- else
- {
- LOGSTREAM("gatewayscc",CCLOG_DEBUG2, stream << "vini." << i << " check hash and vout" << std::endl);
- 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; iInvalid("mismatched inputs != outputs + txfee");
- }
- else return(true);
-}
-
-int64_t GatewaysVerify(char *refdepositaddr,uint256 oracletxid,int32_t claimvout,std::string refcoin,uint256 cointxid,const std::string deposithex,std::vectorproof,uint256 merkleroot,CPubKey destpub,uint8_t taddr,uint8_t prefix,uint8_t prefix2)
-{
- std::vector txids; uint256 proofroot,hashBlock,txid = zeroid; CTransaction tx; std::string name,description,format;
- char destaddr[64],destpubaddr[64],claimaddr[64]; int32_t i,numvouts; int64_t nValue = 0;
-
- if ( myGetTransaction(oracletxid,tx,hashBlock) == 0 || (numvouts= tx.vout.size()) <= 0 )
- {
- LOGSTREAM("gatewayscc",CCLOG_INFO, stream << "GatewaysVerify cant find oracletxid " << oracletxid.GetHex() << std::endl);
- return(0);
- }
- if ( DecodeOraclesCreateOpRet(tx.vout[numvouts-1].scriptPubKey,name,description,format) != 'C' || name != refcoin )
- {
- LOGSTREAM("gatewayscc",CCLOG_INFO, stream << "GatewaysVerify mismatched oracle name " << name << " != " << refcoin << std::endl);
- return(0);
- }
- proofroot = BitcoinGetProofMerkleRoot(proof,txids);
- if ( proofroot != merkleroot )
- {
- LOGSTREAM("gatewayscc",CCLOG_INFO, stream << "GatewaysVerify mismatched merkleroot " << proofroot.GetHex() << " != " << merkleroot.GetHex() << std::endl);
- return(0);
- }
- if (std::find(txids.begin(), txids.end(), cointxid) == txids.end())
- {
- LOGSTREAM("gatewayscc",CCLOG_INFO, stream << "GatewaysVerify invalid proof for this cointxid" << std::endl);
- return 0;
- }
- if ( DecodeHexTx(tx,deposithex) != 0 )
- {
- GetCustomscriptaddress(claimaddr,tx.vout[claimvout].scriptPubKey,taddr,prefix,prefix2);
- GetCustomscriptaddress(destpubaddr,CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG,taddr,prefix,prefix2);
- if ( strcmp(claimaddr,destpubaddr) == 0 )
- {
- for (i=0; i publishers; std::vectortxids; uint256 bindtxid,cointxid; std::vector proof; CPubKey claimpubkey;
- if ( (numvouts= tx.vout.size()) > 0 )
- {
- if ( DecodeGatewaysDepositOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxid,coin,publishers,txids,height,cointxid,claimvout,deposithex,proof,claimpubkey,amount) == 'D' && claimpubkey == mypk )
- {
- return(amount);
- }
- }
- return(0);
-}
-
-int32_t GatewaysBindExists(struct CCcontract_info *cp,CPubKey gatewayspk,uint256 reftokenid)
-{
- 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,wiftype; std::vector pubkeys; CTransaction tx;
- std::vector txids;
-
- _GetCCaddress(markeraddr,EVAL_GATEWAYS,gatewayspk);
- SetCCtxids(txids,markeraddr,true,EVAL_GATEWAYS,zeroid,'B');
- for (std::vector::const_iterator it=txids.begin(); it!=txids.end(); it++)
- {
- if ( myGetTransaction(*it,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 0 && DecodeGatewaysOpRet(tx.vout[numvouts-1].scriptPubKey)=='B' )
- {
- if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) == 'B' )
- {
- if ( tokenid == reftokenid )
- {
- LOGSTREAM("gatewayscc",CCLOG_INFO, stream << "trying to bind an existing tokenid" << std::endl);
- return(1);
- }
- }
- }
- }
- std::vector tmp_txs;
- myGet_mempool_txs(tmp_txs,EVAL_GATEWAYS,'B');
- for (std::vector::const_iterator it=tmp_txs.begin(); it!=tmp_txs.end(); it++)
- {
- const CTransaction &txmempool = *it;
-
- if ((numvouts=txmempool.vout.size()) > 0 && DecodeGatewaysOpRet(tx.vout[numvouts-1].scriptPubKey)=='B')
- if (DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) == 'B' &&
- tokenid == reftokenid)
- return(1);
- }
-
- return(0);
-}
-
-bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx, uint32_t nIn)
-{
- int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks,height,claimvout; bool retval; uint8_t funcid,hash[32],K,M,N,taddr,prefix,prefix2,wiftype;
- char str[65],destaddr[65],depositaddr[65],gatewaystokensaddr[65],validationError[512];
- std::vector txids; std::vector pubkeys,publishers,tmppublishers; std::vector proof; int64_t fullsupply,totalsupply,amount,tmpamount;
- uint256 hashblock,txid,bindtxid,deposittxid,withdrawtxid,completetxid,tokenid,tmptokenid,oracletxid,bindtokenid,cointxid,tmptxid,merkleroot,mhash; CTransaction bindtx,tmptx;
- std::string refcoin,tmprefcoin,hex,name,description,format; CPubKey pubkey,tmppubkey,gatewayspk;
-
- numvins = tx.vin.size();
- numvouts = tx.vout.size();
- preventCCvins = preventCCvouts = -1;
- if ( numvouts < 1 )
- return eval->Invalid("no vouts");
- else
- {
- //LogPrint("gatewayscc-1","check amounts\n");
- // if ( GatewaysExactAmounts(cp,eval,tx,1,10000) == false )
- // {
- // return eval->Invalid("invalid inputs vs. outputs!");
- // }
- // else
- // {
- gatewayspk = GetUnspendable(cp,0);
- GetTokensCCaddress(cp, gatewaystokensaddr, gatewayspk);
- if ( (funcid = DecodeGatewaysOpRet(tx.vout[numvouts-1].scriptPubKey)) != 0)
- {
- switch ( funcid )
- {
- case 'B':
- //vin.0: normal input
- //vin.1: CC input of tokens
- //vout.0: CC vout of gateways tokens to gateways tokens CC address
- //vout.1: CC vout marker
- //vout.n-1: opreturn - 'B' tokenid coin totalsupply oracletxid M N pubkeys taddr prefix prefix2 wiftype
- return eval->Invalid("unexpected GatewaysValidate for gatewaysbind!");
- break;
- case 'D':
- //vin.0: normal input
- //vout.0: CC vout marker to destination pubkey
- //vout.1: normal output marker to txidaddr
- //vout.n-1: opreturn - 'D' bindtxid coin publishers txids height cointxid claimvout deposithex proof destpub amount
- return eval->Invalid("unexpected GatewaysValidate for gatewaysdeposit!");
- break;
- case 'C':
- //vin.0: normal input
- //vin.1: CC input of gateways tokens
- //vin.2: CC input of marker from gatewaysdeposit tx
- //vout.0: CC vout of tokens from deposit amount to destinatoin pubkey
- //vout.1: CC vout change of gateways tokens to gateways tokens CC address (if any)
- //vout.n-1: opreturn - 'C' tokenid bindtxid coin deposittxid destpub amount
- if ((numvouts=tx.vout.size()) < 1 || DecodeGatewaysClaimOpRet(tx.vout[numvouts-1].scriptPubKey,tmptokenid,bindtxid,refcoin,deposittxid,pubkey,amount)!='C')
- return eval->Invalid("invalid gatewaysclaim OP_RETURN data!");
- else if (myGetTransaction(bindtxid,tmptx,hashblock) == 0)
- return eval->Invalid("invalid gatewaysbind txid!");
- else if ((numvouts=tmptx.vout.size()) < 1 || DecodeGatewaysBindOpRet(depositaddr,tmptx.vout[numvouts-1].scriptPubKey,tokenid,tmprefcoin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B')
- return eval->Invalid("invalid gatewaysbind OP_RETURN data!");
- else if ( IsCCInput(tmptx.vin[0].scriptSig) != 0 )
- return eval->Invalid("vin.0 is normal for gatewaysbind!");
- else if ( ConstrainVout(tmptx.vout[0],1,gatewaystokensaddr,totalsupply)==0)
- return eval->Invalid("invalid tokens to gateways vout for gatewaysbind!");
- else if ( ConstrainVout(tmptx.vout[1],1,cp->unspendableCCaddr,CC_MARKER_VALUE)==0)
- return eval->Invalid("invalid marker vout for gatewaysbind!");
- else if (tmprefcoin!=refcoin)
- return eval->Invalid("refcoin different than in bind tx");
- else if (tmptokenid!=tokenid)
- return eval->Invalid("tokenid does not match tokenid from gatewaysbind");
- else if ( N == 0 || N > 15 || M > N )
- return eval->Invalid("invalid MofN in gatewaysbind");
- else if (pubkeys.size()!=N)
- {
- sprintf(validationError,"not enough pubkeys(%ld) for N.%d gatewaysbind ",pubkeys.size(),N);
- return eval->Invalid(validationError);
- }
- else if ( (fullsupply=CCfullsupply(tokenid)) != totalsupply )
- {
- sprintf(validationError,"Gateway bind.%s (%s) globaladdr.%s totalsupply %.8f != fullsupply %.8f\n",refcoin.c_str(),uint256_str(str,tokenid),cp->unspendableCCaddr,(double)totalsupply/COIN,(double)fullsupply/COIN);
- return eval->Invalid(validationError);
- }
- else if (myGetTransaction(oracletxid,tmptx,hashblock) == 0 || (numvouts=tmptx.vout.size()) <= 0 )
- {
- sprintf(validationError,"cant find oracletxid %s\n",uint256_str(str,oracletxid));
- return eval->Invalid(validationError);
- }
- else if ( DecodeOraclesCreateOpRet(tmptx.vout[numvouts-1].scriptPubKey,name,description,format) != 'C' )
- return eval->Invalid("invalid oraclescreate OP_RETURN data");
- else if (refcoin!=name)
- {
- sprintf(validationError,"mismatched oracle name %s != %s\n",name.c_str(),refcoin.c_str());
- return eval->Invalid(validationError);
- }
- else if (format.size()!=3 || strncmp(format.c_str(),"Ihh",3)!=0)
- {
- sprintf(validationError,"illegal format %s != Ihh\n",format.c_str());
- return eval->Invalid(validationError);
- }
- else if (hush_txnotarizedconfirmed(bindtxid) == false)
- return eval->Invalid("gatewaysbind tx is not yet confirmed(notarized)!");
- else if (myGetTransaction(deposittxid,tmptx,hashblock) == 0)
- return eval->Invalid("invalid gatewaysdeposittxid!");
- else if ((numvouts=tmptx.vout.size()) < 1 || DecodeGatewaysDepositOpRet(tmptx.vout[numvouts-1].scriptPubKey,tmptxid,tmprefcoin,tmppublishers,txids,height,cointxid,claimvout,hex,proof,tmppubkey,tmpamount) != 'D')
- return eval->Invalid("invalid gatewaysdeposit OP_RETURN data!");
- else if ( IsCCInput(tmptx.vin[0].scriptSig) != 0 )
- return eval->Invalid("vin.0 is normal for gatewaysdeposit!");
- else if ( GetCCaddress(cp,destaddr,tmppubkey)==0 || ConstrainVout(tmptx.vout[0],1,destaddr,CC_MARKER_VALUE)==0)
- return eval->Invalid("invalid CC marker vout for gatewaysdeposit!");
- else if ( CCtxidaddr(destaddr,cointxid)==CPubKey() || ConstrainVout(tmptx.vout[1],0,destaddr,CC_MARKER_VALUE)==0)
- return eval->Invalid("invalid normal marker vout for gatewaysdeposit!");
- else if (tmprefcoin!=refcoin)
- return eval->Invalid("refcoin different than in deposit tx");
- else if (bindtxid!=tmptxid)
- return eval->Invalid("bindtxid does not match to bindtxid from gatewaysdeposit");
- else if (tmpamount>totalsupply)
- return eval->Invalid("deposit amount greater then bind total supply");
- else if (hush_txnotarizedconfirmed(deposittxid) == false)
- return eval->Invalid("gatewaysdeposit tx is not yet confirmed(notarized)!");
- else if (tx.vin.size()>0)
- {
- i=0;
- while (iInvalid("vin."+std::to_string(i)+" is CC for gatewaysclaim!");
- i++;
- }
- }
- else if ((*cp->ismyvin)(tx.vin[tx.vin.size()-1].scriptSig) == 0 || tmptx.vout[tx.vin[tx.vin.size()-1].prevout.n].nValue!=CC_MARKER_VALUE)
- return eval->Invalid("vin."+std::to_string(tx.vin.size()-1)+" is CC marker for gatewaysclaim or invalid marker amount!");
- else if (_GetCCaddress(destaddr,EVAL_TOKENS,pubkey)==0 || ConstrainVout(tx.vout[0],1,destaddr,amount)==0)
- return eval->Invalid("invalid vout tokens to destpub for gatewaysclaim!");
- else if (numvouts>2 && tx.vout[1].scriptPubKey.IsPayToCryptoCondition() && (myGetTransaction(tx.vin[1].prevout.hash,tmptx,hashblock)==0 || ConstrainVout(tx.vout[1],1,gatewaystokensaddr,tmptx.vout[tx.vin[1].prevout.n].nValue-amount)==0))
- return eval->Invalid("invalid CC change vout for gatewaysclaim!");
- else if (amount!=tmpamount)
- return eval->Invalid("claimed amount different then deposit amount");
- else if (pubkey!=tmppubkey)
- return eval->Invalid("claim destination pubkey different than in deposit tx");
- else
- {
- int32_t m;
- merkleroot = zeroid;
- for (i=m=0; iInvalid(validationError);
- }
- else if (GatewaysVerify(depositaddr,oracletxid,claimvout,tmprefcoin,cointxid,hex,proof,merkleroot,pubkey,taddr,prefix,prefix2)!=amount)
- return eval->Invalid("external deposit not verified\n");
- }
- break;
- case 'W':
- //vin.0: normal input
- //vin.1: CC input of tokens
- //vout.0: CC vout marker to gateways CC address
- //vout.1: CC vout of gateways tokens back to gateways tokens CC address
- //vout.2: CC vout change of tokens back to owners pubkey (if any)
- //vout.n-1: opreturn - 'W' tokenid bindtxid refcoin withdrawpub amount
- return eval->Invalid("unexpected GatewaysValidate for gatewaysWithdraw!");
- break;
- case 'P':
- //vin.0: normal input
- //vin.1: CC input of marker from previous tx (withdraw or partialsing)
- //vout.0: CC vout marker to gateways CC address
- //vout.n-1: opreturn - 'P' withdrawtxid refcoin number_of_signs mypk hex
- if ((numvouts=tx.vout.size()) > 0 && DecodeGatewaysPartialOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,refcoin,K,pubkey,hex)!='P')
- return eval->Invalid("invalid gatewaysPartialSign OP_RETURN data!");
- else if (myGetTransaction(withdrawtxid,tmptx,hashblock) == 0)
- return eval->Invalid("invalid withdraw txid!");
- else if ((numvouts=tmptx.vout.size()) > 0 && DecodeGatewaysWithdrawOpRet(tmptx.vout[numvouts-1].scriptPubKey,tmptokenid,bindtxid,tmprefcoin,pubkey,amount)!='W')
- return eval->Invalid("invalid gatewayswithdraw OP_RETURN data!");
- else if (tmprefcoin!=refcoin)
- return eval->Invalid("refcoin different than in bind tx");
- else if ( IsCCInput(tmptx.vin[0].scriptSig) != 0 )
- return eval->Invalid("vin.0 is normal for gatewaysWithdraw!");
- else if ( ConstrainVout(tmptx.vout[0],1,cp->unspendableCCaddr,CC_MARKER_VALUE)==0)
- return eval->Invalid("invalid marker vout for gatewaysWithdraw!");
- else if ( ConstrainVout(tmptx.vout[1],1,gatewaystokensaddr,amount)==0)
- return eval->Invalid("invalid tokens to gateways vout for gatewaysWithdraw!");
- else if (hush_txnotarizedconfirmed(withdrawtxid) == false)
- return eval->Invalid("gatewayswithdraw tx is not yet confirmed(notarized)!");
- else if (myGetTransaction(bindtxid,tmptx,hashblock) == 0)
- return eval->Invalid("invalid gatewaysbind txid!");
- else if ((numvouts=tmptx.vout.size()) < 1 || DecodeGatewaysBindOpRet(depositaddr,tmptx.vout[numvouts-1].scriptPubKey,tokenid,tmprefcoin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B')
- return eval->Invalid("invalid gatewaysbind OP_RETURN data!");
- else if (tmprefcoin!=refcoin)
- return eval->Invalid("refcoin different than in bind tx");
- else if (tmptokenid!=tokenid)
- return eval->Invalid("tokenid does not match tokenid from gatewaysbind");
- else if (hush_txnotarizedconfirmed(bindtxid) == false)
- return eval->Invalid("gatewaysbind tx is not yet confirmed(notarized)!");
- else if (IsCCInput(tx.vin[0].scriptSig) != 0)
- return eval->Invalid("vin.0 is normal for gatewayspartialsign!");
- else if ((*cp->ismyvin)(tx.vin[tx.vin.size()-1].scriptSig) == 0 || myGetTransaction(tx.vin[tx.vin.size()-1].prevout.hash,tmptx,hashblock)==0 || tmptx.vout[tx.vin[tx.vin.size()-1].prevout.n].nValue!=CC_MARKER_VALUE)
- return eval->Invalid("vin."+std::to_string(tx.vin.size()-1)+" is CC marker for gatewayspartialsign or invalid marker amount!");
- else if (ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,CC_MARKER_VALUE) == 0 )
- return eval->Invalid("vout.0 invalid marker for gatewayspartialsign!");
- else if (K>M)
- return eval->Invalid("invalid number of signs!");
- break;
- case 'S':
- //vin.0: normal input
- //vin.1: CC input of marker from previous tx (withdraw or partialsing)
- //vout.0: CC vout marker to gateways CC address
- //vout.n-1: opreturn - 'S' withdrawtxid refcoin hex
- if ((numvouts=tx.vout.size()) > 0 && DecodeGatewaysCompleteSigningOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,refcoin,K,hex)!='S')
- return eval->Invalid("invalid gatewayscompletesigning OP_RETURN data!");
- else if (myGetTransaction(withdrawtxid,tmptx,hashblock) == 0)
- return eval->Invalid("invalid withdraw txid!");
- else if ((numvouts=tmptx.vout.size()) > 0 && DecodeGatewaysWithdrawOpRet(tmptx.vout[numvouts-1].scriptPubKey,tmptokenid,bindtxid,tmprefcoin,pubkey,amount)!='W')
- return eval->Invalid("invalid gatewayswithdraw OP_RETURN data!");
- else if (tmprefcoin!=refcoin)
- return eval->Invalid("refcoin different than in bind tx");
- else if ( IsCCInput(tmptx.vin[0].scriptSig) != 0 )
- return eval->Invalid("vin.0 is normal for gatewaysWithdraw!");
- else if ( ConstrainVout(tmptx.vout[0],1,cp->unspendableCCaddr,CC_MARKER_VALUE)==0)
- return eval->Invalid("invalid marker vout for gatewaysWithdraw!");
- else if ( ConstrainVout(tmptx.vout[1],1,gatewaystokensaddr,amount)==0)
- return eval->Invalid("invalid tokens to gateways vout for gatewaysWithdraw!");
- else if (tmptx.vout[1].nValue!=amount)
- return eval->Invalid("amount in opret not matching tx tokens amount!");
- else if (hush_txnotarizedconfirmed(withdrawtxid) == false)
- return eval->Invalid("gatewayswithdraw tx is not yet confirmed(notarized)!");
- else if (myGetTransaction(bindtxid,tmptx,hashblock) == 0)
- return eval->Invalid("invalid gatewaysbind txid!");
- else if ((numvouts=tmptx.vout.size()) < 1 || DecodeGatewaysBindOpRet(depositaddr,tmptx.vout[numvouts-1].scriptPubKey,tokenid,tmprefcoin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B')
- return eval->Invalid("invalid gatewaysbind OP_RETURN data!");
- else if (tmprefcoin!=refcoin)
- return eval->Invalid("refcoin different than in bind tx");
- else if (tmptokenid!=tokenid)
- return eval->Invalid("tokenid does not match tokenid from gatewaysbind");
- else if (hush_txnotarizedconfirmed(bindtxid) == false)
- return eval->Invalid("gatewaysbind tx is not yet confirmed(notarized)!");
- else if (IsCCInput(tx.vin[0].scriptSig) != 0)
- return eval->Invalid("vin.0 is normal for gatewayscompletesigning!");
- else if ((*cp->ismyvin)(tx.vin[tx.vin.size()-1].scriptSig) == 0 || myGetTransaction(tx.vin[tx.vin.size()-1].prevout.hash,tmptx,hashblock)==0 || tmptx.vout[tx.vin[tx.vin.size()-1].prevout.n].nValue!=CC_MARKER_VALUE)
- return eval->Invalid("vin."+std::to_string(tx.vin.size()-1)+" is CC marker for gatewayscompletesigning or invalid marker amount!");
- else if (ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,CC_MARKER_VALUE) == 0 )
- return eval->Invalid("vout.0 invalid marker for gatewayscompletesigning!");
- else if (KInvalid("invalid number of signs!");
- break;
- case 'M':
- //vin.0: normal input
- //vin.1: CC input of gatewayscompletesigning tx marker to gateways CC address
- //vout.0: opreturn - 'M' withdrawtxid refcoin completetxid
- if ((numvouts=tx.vout.size()) > 0 && DecodeGatewaysMarkDoneOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,refcoin,completetxid)!='M')
- return eval->Invalid("invalid gatewaysmarkdone OP_RETURN data!");
- else if (myGetTransaction(completetxid,tmptx,hashblock) == 0)
- return eval->Invalid("invalid gatewayscompletesigning txid!");
- else if ((numvouts=tmptx.vout.size()) > 0 && DecodeGatewaysCompleteSigningOpRet(tmptx.vout[numvouts-1].scriptPubKey,withdrawtxid,tmprefcoin,K,hex)!='S')
- return eval->Invalid("invalid gatewayscompletesigning OP_RETURN data!");
- else if (hush_txnotarizedconfirmed(completetxid) == false)
- return eval->Invalid("gatewayscompletesigning tx is not yet confirmed(notarized)!");
- else if (myGetTransaction(withdrawtxid,tmptx,hashblock) == 0)
- return eval->Invalid("invalid withdraw txid!");
- else if ((numvouts=tmptx.vout.size()) > 0 && DecodeGatewaysWithdrawOpRet(tmptx.vout[numvouts-1].scriptPubKey,tmptokenid,bindtxid,tmprefcoin,pubkey,amount)!='W')
- return eval->Invalid("invalid gatewayswithdraw OP_RETURN data!");
- else if (tmprefcoin!=refcoin)
- return eval->Invalid("refcoin different than in bind tx");
- else if (hush_txnotarizedconfirmed(withdrawtxid) == false)
- return eval->Invalid("gatewayswithdraw tx is not yet confirmed(notarized)!");
- else if (myGetTransaction(bindtxid,tmptx,hashblock) == 0)
- return eval->Invalid("invalid gatewaysbind txid!");
- else if ((numvouts=tmptx.vout.size()) < 1 || DecodeGatewaysBindOpRet(depositaddr,tmptx.vout[numvouts-1].scriptPubKey,tokenid,tmprefcoin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B')
- return eval->Invalid("invalid gatewaysbind OP_RETURN data!");
- else if (tmprefcoin!=refcoin)
- return eval->Invalid("refcoin different than in bind tx");
- else if (tmptokenid!=tokenid)
- return eval->Invalid("tokenid does not match tokenid from gatewaysbind");
- else if (hush_txnotarizedconfirmed(bindtxid) == false)
- return eval->Invalid("gatewaysbind tx is not yet confirmed(notarized)!");
- else if ( IsCCInput(tx.vin[0].scriptSig) != 0 )
- return eval->Invalid("vin.0 is normal for gatewaysmarkdone!");
- else if ((*cp->ismyvin)(tx.vin[tx.vin.size()-1].scriptSig) == 0 || myGetTransaction(tx.vin[tx.vin.size()-1].prevout.hash,tmptx,hashblock)==0 || tmptx.vout[tx.vin[tx.vin.size()-1].prevout.n].nValue!=CC_MARKER_VALUE)
- return eval->Invalid("vin."+std::to_string(tx.vin.size()-1)+" is CC marker for gatewaysmarkdone or invalid marker amount!");
- else if (KInvalid("invalid number of signs!");
- break;
- }
- }
- retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts);
- if ( retval != 0 )
- LOGSTREAM("gatewayscc",CCLOG_INFO, stream << "Gateways tx validated" << std::endl);
- else fprintf(stderr,"Gateways tx 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,uint256 bindtxid,int64_t total,int32_t maxinputs)
-{
- char coinaddr[64],depositaddr[64]; int64_t threshold,nValue,price,totalinputs = 0,totalsupply,amount;
- CTransaction vintx,bindtx; int32_t vout,numvouts,n = 0; uint8_t M,N,evalcode,funcid,taddr,prefix,prefix2,wiftype; std::vector pubkeys;
- std::vector > unspentOutputs; std::string refcoin,tmprefcoin; CPubKey withdrawpub,destpub;
- uint256 tokenid,txid,oracletxid,tmpbindtxid,tmptokenid,deposittxid,hashBlock;
-
- if ( myGetTransaction(bindtxid,bindtx,hashBlock) != 0 )
- {
- if ((numvouts=bindtx.vout.size())!=0 && DecodeGatewaysBindOpRet(depositaddr,bindtx.vout[numvouts-1].scriptPubKey,tokenid,refcoin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) == 'B')
- {
- GetTokensCCaddress(cp,coinaddr,pk);
- SetCCunspents(unspentOutputs,coinaddr,true);
- if ( maxinputs > CC_MAXVINS )
- maxinputs = CC_MAXVINS;
- if ( maxinputs > 0 )
- threshold = total/maxinputs;
- else threshold = total;
- LOGSTREAM("gatewayscc",CCLOG_DEBUG1, stream << "check " << coinaddr << " for gateway inputs" << std::endl);
- for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
- {
- txid = it->first.txhash;
- vout = (int32_t)it->first.index;
- if ( myGetTransaction(txid,vintx,hashBlock) != 0 )
- {
- funcid=DecodeGatewaysOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey);
- if ((vout==0 && funcid=='B' && bindtxid==txid && total != 0 && maxinputs != 0) ||
- (vout==1 && funcid=='W' && DecodeGatewaysWithdrawOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,tmptokenid,tmpbindtxid,tmprefcoin,withdrawpub,amount) == 'W' &&
- tmpbindtxid==bindtxid && tmprefcoin==refcoin && tmptokenid==tokenid && total != 0 && maxinputs != 0) ||
- (vout==1 && funcid=='C' && DecodeGatewaysClaimOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,tmptokenid,tmpbindtxid,tmprefcoin,deposittxid,destpub,amount) == 'C' &&
- tmpbindtxid==bindtxid && tmprefcoin==refcoin && tmptokenid==tokenid && total != 0 && maxinputs != 0))
- {
- mtx.vin.push_back(CTxIn(txid,vout,CScript()));
- totalinputs += it->second.satoshis;
- n++;
- if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs)) break;
- }
- }
- }
- return(totalinputs);
- }
- else LOGSTREAM("gatewayscc",CCLOG_INFO, stream << "invalid GatewaysBind" << std::endl);
- }
- else LOGSTREAM("gatewayscc",CCLOG_INFO, stream << "can't find GatewaysBind txid" << std::endl);
- return(0);
-}
-
-UniValue GatewaysBind(const CPubKey& pk, uint64_t txfee,std::string coin,uint256 tokenid,int64_t totalsupply,uint256 oracletxid,uint8_t M,uint8_t N,std::vector pubkeys,uint8_t p1,uint8_t p2,uint8_t p3,uint8_t p4)
-{
- CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
- CTransaction oracletx; uint8_t taddr,prefix,prefix2,wiftype; CPubKey mypk,gatewayspk; CScript opret; uint256 hashBlock;
- struct CCcontract_info *cp,*cpTokens,C,CTokens; std::string name,description,format; int32_t i,numvouts; int64_t fullsupply;
- char destaddr[64],coinaddr[64],myTokenCCaddr[64],*fstr;
-
- cp = CCinit(&C,EVAL_GATEWAYS);
- cpTokens = CCinit(&CTokens,EVAL_TOKENS);
- if (coin=="HUSH")
- {
- prefix = HUSH_PUBTYPE;
- prefix2 = HUSH_P2SHTYPE;
- wiftype = HUSH_WIFTYPE;
- taddr = HUSH_TADDR;
- }
- else
- {
- prefix = p1;
- prefix2 = p2;
- wiftype = p3;
- taddr = p4;
- LOGSTREAM("gatewayscc",CCLOG_DEBUG1, stream << "set prefix " << prefix << ", prefix2 " << prefix2 << ", wiftype " << wiftype << ", taddr " << taddr << " for " << coin << std::endl);
- }
- if ( N == 0 || N > 15 || M > N )
- CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "illegal M." << M << " or N." << N);
- if ( pubkeys.size() != N )
- CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "M."<< M << " N." << N << " but pubkeys[" <<( int32_t)pubkeys.size() << "]");
- for (i=0; iunspendableCCaddr << " totalsupply " << (double)totalsupply/COIN << " != fullsupply " << (double)fullsupply/COIN);
- if ( CCtoken_balance(myTokenCCaddr,tokenid) != totalsupply )
- CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "token balance on " << myTokenCCaddr << " " << (double)CCtoken_balance((char *)myTokenCCaddr,tokenid)/COIN << "!=" << (double)totalsupply/COIN);
- if ( myGetTransaction(oracletxid,oracletx,hashBlock) == 0 || (numvouts= oracletx.vout.size()) <= 0 )
- CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "cant find oracletxid " << oracletxid.GetHex());
- if ( DecodeOraclesCreateOpRet(oracletx.vout[numvouts-1].scriptPubKey,name,description,format) != 'C' )
- CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "mismatched oracle name " << name << " != " << coin);
- if ( (fstr=(char *)format.c_str()) == 0 || strncmp(fstr,"Ihh",3) != 0 )
- CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "illegal format (" << fstr << ") != (Ihh)");
- if ( GatewaysBindExists(cp,gatewayspk,tokenid) != 0 )
- CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "Gateway bind." << coin << " (" << tokenid.GetHex() << ") already exists");
- if ( AddNormalinputs(mtx,mypk,txfee+CC_MARKER_VALUE,2,pk.IsValid()) > 0 )
- {
- if (AddTokenCCInputs(cpTokens, mtx, mypk, tokenid, totalsupply, 64)>0)
- {
- mtx.vout.push_back(MakeTokensCC1vout(cp->evalcode,totalsupply,gatewayspk));
- mtx.vout.push_back(MakeCC1vout(cp->evalcode,CC_MARKER_VALUE,gatewayspk));
- return(FinalizeCCTxExt(pk.IsValid(),0,cp,mtx,mypk,txfee,EncodeGatewaysBindOpRet('B',tokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype)));
- }
- }
- CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "cant find enough inputs");
-}
-
-UniValue GatewaysDeposit(const CPubKey& pk, uint64_t txfee,uint256 bindtxid,int32_t height,std::string refcoin,uint256 cointxid,int32_t claimvout,std::string deposithex,std::vectorproof,CPubKey destpub,int64_t amount)
-{
- CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
- CTransaction tx; CPubKey mypk; uint256 oracletxid,merkleroot,mhash,hashBlock,tokenid,txid;
- int64_t totalsupply; int32_t i,m,n,numvouts; uint8_t M,N,taddr,prefix,prefix2,wiftype; std::string coin; struct CCcontract_info *cp,C;
- std::vector pubkeys,publishers; std::vectortxids; char str[65],depositaddr[64],txidaddr[64];
-
- cp = CCinit(&C,EVAL_GATEWAYS);
- if ( txfee == 0 )
- txfee = 10000;
- mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey());
- LOGSTREAM("gatewayscc",CCLOG_DEBUG1, stream << "GatewaysDeposit ht." << height << " " << refcoin << " " << (double)amount/COIN << " numpks." << (int32_t)pubkeys.size() << std::endl);
- if ( myGetTransaction(bindtxid,tx,hashBlock) == 0 || (numvouts= tx.vout.size()) <= 0 )
- CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "cant find bindtxid " << bindtxid.GetHex());
- if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B' || refcoin != coin )
- CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "invalid coin - bindtxid " << bindtxid.GetHex() << " coin." << coin);
- if (hush_txnotarizedconfirmed(bindtxid)==false)
- CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "gatewaysbind tx not yet confirmed/notarized");
- n = (int32_t)pubkeys.size();
- merkleroot = zeroid;
- for (i=m=0; i 0 )
- {
- mtx.vout.push_back(MakeCC1vout(cp->evalcode,CC_MARKER_VALUE,destpub));
- mtx.vout.push_back(CTxOut(CC_MARKER_VALUE,CScript() << ParseHex(HexStr(CCtxidaddr(txidaddr,cointxid))) << OP_CHECKSIG));
- return(FinalizeCCTxExt(pk.IsValid(),0,cp,mtx,mypk,txfee,EncodeGatewaysDepositOpRet('D',bindtxid,coin,publishers,txids,height,cointxid,claimvout,deposithex,proof,destpub,amount)));
- }
- CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "cant find enough inputs");
-}
-
-UniValue GatewaysClaim(const CPubKey& pk, uint64_t txfee,uint256 bindtxid,std::string refcoin,uint256 deposittxid,CPubKey destpub,int64_t amount)
-{
- CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
- CTransaction tx; CPubKey mypk,gatewayspk,tmpdestpub; struct CCcontract_info *cp,C; uint8_t M,N,taddr,prefix,prefix2,wiftype;
- std::string coin, deposithex; std::vector pubkeys,publishers; int64_t totalsupply,depositamount,tmpamount,inputs,CCchange=0;
- int32_t numvouts,claimvout,height; std::vector proof;
- uint256 hashBlock,tokenid,oracletxid,tmptxid,cointxid; char depositaddr[64],coinaddr[64],destaddr[64]; std::vector txids;
-
- cp = CCinit(&C,EVAL_GATEWAYS);
- if ( txfee == 0 )
- txfee = 10000;
- mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey());
- gatewayspk = GetUnspendable(cp,0);
- if ( myGetTransaction(bindtxid,tx,hashBlock) == 0 || (numvouts= tx.vout.size()) <= 0 )
- CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "cant find bindtxid " << bindtxid.GetHex());
- if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B' || refcoin != coin )
- CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "invalid coin - bindtxid " << bindtxid.GetHex() << " coin." << coin);
- if (hush_txnotarizedconfirmed(bindtxid)==false)
- CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "gatewaysbind tx not yet confirmed/notarized");
- if ( myGetTransaction(deposittxid,tx,hashBlock) == 0 || (numvouts= tx.vout.size()) <= 0 )
- CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "cant find deposittxid " << bindtxid.GetHex());
- if (DecodeGatewaysDepositOpRet(tx.vout[numvouts-1].scriptPubKey,tmptxid,coin,publishers,txids,height,cointxid,claimvout,deposithex,proof,tmpdestpub,tmpamount) != 'D' || coin != refcoin)
- CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "invalid coin - deposittxid " << bindtxid.GetHex() << " coin." << coin);
- if (hush_txnotarizedconfirmed(deposittxid)==false)
- CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "gatewaysdeposit tx not yet confirmed/notarized");
- if (tmpdestpub!=destpub)
- CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "different destination pubkey from desdeposit tx");
- if ( (depositamount=GatewaysDepositval(tx,mypk)) != amount )
- CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "invalid Gateways deposittxid " << deposittxid.GetHex() << " " << (double)depositamount/COIN << " != " << (double)amount/COIN << ", remember claim must be done from destination pubkey from deposit tx!");
- if ((inputs=AddGatewaysInputs(cp, mtx, gatewayspk, bindtxid, amount, 60)) > 0)
- {
- if ( inputs > amount ) CCchange = (inputs - amount);
- mtx.vin.push_back(CTxIn(deposittxid,0,CScript()));
- mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS,amount,destpub));
- if ( CCchange != 0 ) mtx.vout.push_back(MakeTokensCC1vout(EVAL_GATEWAYS,CCchange,gatewayspk));
- return(FinalizeCCTxExt(pk.IsValid(),0,cp,mtx,mypk,txfee,EncodeGatewaysClaimOpRet('C',tokenid,bindtxid,refcoin,deposittxid,destpub,amount)));
- }
- CCERR_RESULT("gatewayscc",CCLOG_INFO, stream << "cant find enough tokens in gateways address for given amount");
-}
-
-UniValue GatewaysWithdraw(const CPubKey& pk, uint64_t txfee,uint256 bindtxid,std::string refcoin,CPubKey withdrawpub,int64_t amount)
-{
- CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
- CTransaction tx; CPubKey mypk,gatewayspk,signerpk; uint256 txid,tokenid,hashBlock,oracletxid,tmptokenid,tmpbindtxid,withdrawtxid; int32_t vout,numvouts;
- int64_t nValue,totalsupply,inputs,CCchange=0,tmpamount; uint8_t funcid,K,M,N,taddr,prefix,prefix2,wiftype; std::string coin,hex;
- std::vector