From 259ad7b1bb4e5bfa0259d656c9eed303d937aeef Mon Sep 17 00:00:00 2001 From: Dennis Fantoni Date: Sun, 9 Nov 2014 19:16:13 +0100 Subject: [PATCH 01/11] added run_repeat.bat that restarts the wallet automatically if it crashes, run_repeat.bat also have an easy way to set the memory ceiling for the wallet File is saved with windows line endings to enable notepad users to edit it. --- run_repeat.bat | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 run_repeat.bat diff --git a/run_repeat.bat b/run_repeat.bat new file mode 100644 index 000000000..b2fb7f3e8 --- /dev/null +++ b/run_repeat.bat @@ -0,0 +1,21 @@ +title BURST WALLET +set MAXRAM=700m +set LAUNCH=-Xmx%MAXRAM% -cp burst.jar;lib\*;conf nxt.Nxt +set JAVAEXEC=\Java\jre7\bin\java.exe +:runagain +@ECHO OFF +IF EXIST java ( + java %LAUNCH% +) ELSE ( + IF EXIST "%PROGRAMFILES%\Java\jre7" ( + "%PROGRAMFILES%%JAVAEXEC%" %LAUNCH% + ) ELSE ( + IF EXIST "%PROGRAMFILES(X86)%\Java\jre7" ( + "%PROGRAMFILES(X86)%%JAVAEXEC%" %LAUNCH% + ) ELSE ( + ECHO Java software not found on your system. Please go to http://java.com/en/ to download a copy of Java. + PAUSE + ) + ) +) +goto runagain \ No newline at end of file From f19182d5fa74f448b11eabcb6e2bdfae5f090d81 Mon Sep 17 00:00:00 2001 From: Dennis Fantoni Date: Sun, 9 Nov 2014 20:10:28 +0100 Subject: [PATCH 02/11] compile.bat that compiles under windows with no dependencies on 3rdparty shell software --- compile.bat | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 compile.bat diff --git a/compile.bat b/compile.bat new file mode 100644 index 000000000..76eb7193a --- /dev/null +++ b/compile.bat @@ -0,0 +1,16 @@ +set CP=conf\;classes\;lib\* +set SP=src\java\ + +md classes + +"C:\Program Files\Java\jdk1.7.0_67\bin\javac.exe" -sourcepath %SP% -classpath %CP% -d classes\ src\java\nxt\crypto\*.java src\java\nxt\util\*.java src\java\nxt\http\*.java src\java\fr\cryptohash\*.java + +:/bin/rm -f burst.jar +:this seems to be overwritten by the compiler so not really any need to remove it on windows + +"C:\Program Files\Java\jdk1.7.0_67\bin\jar.exe" cf burst.jar -C classes . +:/bin/rm -rf classes +:seems to update fine even if not removed, so no immediate need to remove the directory + +echo "burst.jar generated successfully" +pause From aec2b65cc83f6cd682321b6fee28d0490b1e2bee Mon Sep 17 00:00:00 2001 From: Dennis Fantoni Date: Sun, 9 Nov 2014 20:26:33 +0100 Subject: [PATCH 03/11] Added a printout to console every time the wallet gets a deadline below 1 million from a miner. This way a user does not have to look at 10 miner windows but just one wallet window to see if a low deadline shows up. Also useful to spot very high deadlines, that may be indicative of configuration issues. nonce is also printed to assist identifying the plot file. It would be nice if the message was routed via the logfile system but i don't know how to do that from within SubmitNonce --- src/java/nxt/http/SubmitNonce.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/java/nxt/http/SubmitNonce.java b/src/java/nxt/http/SubmitNonce.java index 16832f683..b75ac3b03 100644 --- a/src/java/nxt/http/SubmitNonce.java +++ b/src/java/nxt/http/SubmitNonce.java @@ -94,7 +94,11 @@ else if(assignment.getFromHeight() > Nxt.getBlockchain().getLastBlock().getHeigh response.put("result", "failed to create generator"); return response; } - + //this will write all interesting deadlines submitted to the wallet from miners to the console. + //Should probably go via the logging system but i don't think it is accessible from here currently + BigInteger deadline = generator.getDeadline(); + if (deadline < 1000000) + System.out.println("Block:"+(Nxt.getBlockchain().getLastBlock().getHeight() + 1)+" Nonce: "+String.format("%15s",nonce)+ " Deadline:" + String.format("%15s",deadline)); //response.put("result", "deadline: " + generator.getDeadline()); response.put("result", "success"); response.put("deadline", generator.getDeadline()); From e7f714232f15b1d101928913e0a1879a489b44ee Mon Sep 17 00:00:00 2001 From: Dennis Fantoni Date: Sun, 9 Nov 2014 23:16:44 +0100 Subject: [PATCH 04/11] fixed compile bug in deadline printing. Now prints out deadlines if they are 1M or lower, also prints out block# and nonce. Printout is not going via the log system which it ought to do, but i don't know how to hook up to it right now, and this system console output thing works fine enough for my own use. --- src/java/nxt/http/SubmitNonce.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/java/nxt/http/SubmitNonce.java b/src/java/nxt/http/SubmitNonce.java index b75ac3b03..8be9c805f 100644 --- a/src/java/nxt/http/SubmitNonce.java +++ b/src/java/nxt/http/SubmitNonce.java @@ -1,7 +1,7 @@ package nxt.http; import java.nio.ByteBuffer; - +import java.math.BigInteger; //used to write deadline import javax.servlet.http.HttpServletRequest; import nxt.Account; @@ -96,12 +96,14 @@ else if(assignment.getFromHeight() > Nxt.getBlockchain().getLastBlock().getHeigh } //this will write all interesting deadlines submitted to the wallet from miners to the console. //Should probably go via the logging system but i don't think it is accessible from here currently + BigInteger deadline = generator.getDeadline(); - if (deadline < 1000000) - System.out.println("Block:"+(Nxt.getBlockchain().getLastBlock().getHeight() + 1)+" Nonce: "+String.format("%15s",nonce)+ " Deadline:" + String.format("%15s",deadline)); + BigInteger maxToReport = BigInteger.valueOf(1000000); + if (maxToReport.compareTo( deadline) >0){ + System.out.println("Block:"+(Nxt.getBlockchain().getLastBlock().getHeight() + 1)+" Nonce: "+String.format("%15s",nonce)+ " Deadline:" + String.format("%15s",deadline));} //response.put("result", "deadline: " + generator.getDeadline()); response.put("result", "success"); - response.put("deadline", generator.getDeadline()); + response.put("deadline", deadline); return response; } From e4deebf5bbf98f2110cf20b6f2a94b9b0b92b719 Mon Sep 17 00:00:00 2001 From: Dennis Fantoni Date: Sun, 9 Nov 2014 23:55:42 +0100 Subject: [PATCH 05/11] Added deadline printing to the loggin system. Now an info log line is created every time a nonce with a deadline lower than 1 million is sent to the wallet. Info log line is still block#, Nonce, Deadline in seconds. --- src/java/nxt/http/SubmitNonce.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/java/nxt/http/SubmitNonce.java b/src/java/nxt/http/SubmitNonce.java index 8be9c805f..5b7b9fd7b 100644 --- a/src/java/nxt/http/SubmitNonce.java +++ b/src/java/nxt/http/SubmitNonce.java @@ -2,6 +2,7 @@ import java.nio.ByteBuffer; import java.math.BigInteger; //used to write deadline +import nxt.util.Logger;//used to write deadline import javax.servlet.http.HttpServletRequest; import nxt.Account; @@ -100,7 +101,8 @@ else if(assignment.getFromHeight() > Nxt.getBlockchain().getLastBlock().getHeigh BigInteger deadline = generator.getDeadline(); BigInteger maxToReport = BigInteger.valueOf(1000000); if (maxToReport.compareTo( deadline) >0){ - System.out.println("Block:"+(Nxt.getBlockchain().getLastBlock().getHeight() + 1)+" Nonce: "+String.format("%15s",nonce)+ " Deadline:" + String.format("%15s",deadline));} + String log_msg = "Block:"+(Nxt.getBlockchain().getLastBlock().getHeight() + 1)+" Nonce: "+String.format("%15s",nonce)+ " Deadline:" + String.format("%15s",deadline); + Logger.logMessage(log_msg);} //response.put("result", "deadline: " + generator.getDeadline()); response.put("result", "success"); response.put("deadline", deadline); From 1ef4497c366e3c9b686f5f6d26bc6a528342ee91 Mon Sep 17 00:00:00 2001 From: Dennis Fantoni Date: Mon, 10 Nov 2014 00:00:32 +0100 Subject: [PATCH 06/11] spaced deadline number closer to its text --- src/java/nxt/http/SubmitNonce.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java/nxt/http/SubmitNonce.java b/src/java/nxt/http/SubmitNonce.java index 5b7b9fd7b..8a06f2b13 100644 --- a/src/java/nxt/http/SubmitNonce.java +++ b/src/java/nxt/http/SubmitNonce.java @@ -101,7 +101,7 @@ else if(assignment.getFromHeight() > Nxt.getBlockchain().getLastBlock().getHeigh BigInteger deadline = generator.getDeadline(); BigInteger maxToReport = BigInteger.valueOf(1000000); if (maxToReport.compareTo( deadline) >0){ - String log_msg = "Block:"+(Nxt.getBlockchain().getLastBlock().getHeight() + 1)+" Nonce: "+String.format("%15s",nonce)+ " Deadline:" + String.format("%15s",deadline); + String log_msg = "Block:"+(Nxt.getBlockchain().getLastBlock().getHeight() + 1)+" Nonce: "+String.format("%15s",nonce)+ " Deadline:" + String.format("%8s",deadline); Logger.logMessage(log_msg);} //response.put("result", "deadline: " + generator.getDeadline()); response.put("result", "success"); From 682bff1770013450ad2756a8b2c8f262218a787d Mon Sep 17 00:00:00 2001 From: Dennis Fantoni Date: Thu, 13 Nov 2014 20:42:25 +0100 Subject: [PATCH 07/11] added setting in nxt-default.properties that determines if information about low deadlines are logged, and also how low a deadline must be, before it is logged. Default is 0 which means no logging, so backward compatible. also gave DumpPeersVersion a value of false instead of just nothing --- conf/nxt-default.properties | 9 ++++++++- src/java/nxt/http/SubmitNonce.java | 21 ++++++++++++++------- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/conf/nxt-default.properties b/conf/nxt-default.properties index dc03ccd7c..f17043e59 100644 --- a/conf/nxt-default.properties +++ b/conf/nxt-default.properties @@ -1,5 +1,6 @@ #### PEER NETWORKING #### + # Announce my IP address/hostname to peers and allow them to share it with other peers. # If disabled, peer networking servlet will not be started at all. nxt.shareMyAddress=true @@ -199,6 +200,12 @@ nxt.debugTraceQuote=" # Log changes to unconfirmed balances. nxt.debugLogUnconfirmed=false +# If set to larger than 0 then all deadlines recieved from miners, that are smaller than or equal to this number will be logged +# so to see all deadlines smaller than 200000 in the wallet window and in the log, change the 0 to 200000 +# a log line will look like this : +# 2014-11-13 20:27:29 INFO: Block:33730 Nonce: 157212105 Deadline: 22100 +nxt.MaxDeadlineToLog=0 + #### DATABASE #### @@ -247,7 +254,7 @@ nxt.forceValidate=false nxt.forceScan=false # Print a list of peers having this version on exit. -nxt.dumpPeersVersion= +nxt.dumpPeersVersion=false # Enable trimming of derived objects tables. nxt.trimDerivedTables=true diff --git a/src/java/nxt/http/SubmitNonce.java b/src/java/nxt/http/SubmitNonce.java index 8a06f2b13..beb1acc85 100644 --- a/src/java/nxt/http/SubmitNonce.java +++ b/src/java/nxt/http/SubmitNonce.java @@ -20,6 +20,8 @@ public final class SubmitNonce extends APIServlet.APIRequestHandler { static final SubmitNonce instance = new SubmitNonce(); + static final int MaxDeadlineToLog = Nxt.getIntProperty("nxt.MaxDeadlineToLog"); //potential bug:you cannot set the max to more than what fits in an int + private SubmitNonce() { super(new APITag[] {APITag.MINING}, "secretPhrase", "nonce", "accountId"); } @@ -95,14 +97,19 @@ else if(assignment.getFromHeight() > Nxt.getBlockchain().getLastBlock().getHeigh response.put("result", "failed to create generator"); return response; } - //this will write all interesting deadlines submitted to the wallet from miners to the console. - //Should probably go via the logging system but i don't think it is accessible from here currently - + //this will write all interesting deadlines submitted to the wallet from miners to the console. + //to enable the feature, add + //MaxDeadlineToLog=100000 + //to the config file - the number 100000 means that all deadlines with a value below that, gets logged BigInteger deadline = generator.getDeadline(); - BigInteger maxToReport = BigInteger.valueOf(1000000); - if (maxToReport.compareTo( deadline) >0){ - String log_msg = "Block:"+(Nxt.getBlockchain().getLastBlock().getHeight() + 1)+" Nonce: "+String.format("%15s",nonce)+ " Deadline:" + String.format("%8s",deadline); - Logger.logMessage(log_msg);} + + if (MaxDeadlineToLog!=0){ + BigInteger maxToReport = BigInteger.valueOf(MaxDeadlineToLog); + if (maxToReport.compareTo( deadline) >0){ + String log_msg = "Block:"+(Nxt.getBlockchain().getLastBlock().getHeight() + 1)+" Nonce: "+String.format("%15s",nonce)+ " Deadline:" + String.format("%8s",deadline); + Logger.logMessage(log_msg); + } + } //response.put("result", "deadline: " + generator.getDeadline()); response.put("result", "success"); response.put("deadline", deadline); From dcbe0437806b55041ed050dae304ce3b5e7ee584 Mon Sep 17 00:00:00 2001 From: Dennis Fantoni Date: Mon, 22 Dec 2014 07:28:06 +0100 Subject: [PATCH 08/11] updated to 1.2.0 and merged origin 1.2.0 into my branch, as my pr has not yet been taking into the main branch. corrected spelling error in the config file --- conf/nxt-default.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/nxt-default.properties b/conf/nxt-default.properties index f17043e59..70a8ce31f 100644 --- a/conf/nxt-default.properties +++ b/conf/nxt-default.properties @@ -200,7 +200,7 @@ nxt.debugTraceQuote=" # Log changes to unconfirmed balances. nxt.debugLogUnconfirmed=false -# If set to larger than 0 then all deadlines recieved from miners, that are smaller than or equal to this number will be logged +# If set to larger than 0 then all deadlines received from miners, that are smaller than or equal to this number will be logged # so to see all deadlines smaller than 200000 in the wallet window and in the log, change the 0 to 200000 # a log line will look like this : # 2014-11-13 20:27:29 INFO: Block:33730 Nonce: 157212105 Deadline: 22100 From c0cb2c0ebb68ca8d2141e0f364a357adc875589a Mon Sep 17 00:00:00 2001 From: Dennis Fantoni Date: Wed, 28 Jan 2015 00:13:43 +0100 Subject: [PATCH 09/11] added missing directories to compile.bat making my window batch based builds work again --- compile.bat | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compile.bat b/compile.bat index 76eb7193a..be1dde344 100644 --- a/compile.bat +++ b/compile.bat @@ -3,7 +3,8 @@ set SP=src\java\ md classes -"C:\Program Files\Java\jdk1.7.0_67\bin\javac.exe" -sourcepath %SP% -classpath %CP% -d classes\ src\java\nxt\crypto\*.java src\java\nxt\util\*.java src\java\nxt\http\*.java src\java\fr\cryptohash\*.java +"C:\Program Files\Java\jdk1.7.0_67\bin\javac.exe" -sourcepath %SP% -classpath %CP% -d classes\ src\java\nxt\*.java src\java\nxt\crypto\*.java src\java\nxt\util\*.java src\java\nxt\http\*.java src\java\fr\cryptohash\*.java src\java\nxt\at\*.java src\java\nxt\peer\*.java src\java\nxt\user\*.java src\java\nxt\db\*.java src\java\fr\cryptohash\test\*.java + :/bin/rm -f burst.jar :this seems to be overwritten by the compiler so not really any need to remove it on windows From 87b2cf1c6a4ac548b973f2ba8be4e3b9c0908b5d Mon Sep 17 00:00:00 2001 From: Dennis Fantoni Date: Sun, 8 Feb 2015 01:16:12 +0100 Subject: [PATCH 10/11] update from 1.2.1 to 1.2.2 using burstcoin. also updated run_repeat.bat to use newest java 8 version, and made it easier to adapt run_repeat.bat in case you update to an even newer version of java. --- run_repeat.bat | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/run_repeat.bat b/run_repeat.bat index b2fb7f3e8..2f45a4b61 100644 --- a/run_repeat.bat +++ b/run_repeat.bat @@ -1,16 +1,17 @@ title BURST WALLET set MAXRAM=700m set LAUNCH=-Xmx%MAXRAM% -cp burst.jar;lib\*;conf nxt.Nxt -set JAVAEXEC=\Java\jre7\bin\java.exe +set JAVAVERSION=jre1.8.0_31 +set JAVAEXEC=\Java\%JAVAVERSION%\bin\java.exe :runagain @ECHO OFF IF EXIST java ( java %LAUNCH% ) ELSE ( - IF EXIST "%PROGRAMFILES%\Java\jre7" ( + IF EXIST "%PROGRAMFILES%\Java\%JAVAVERSION%" ( "%PROGRAMFILES%%JAVAEXEC%" %LAUNCH% ) ELSE ( - IF EXIST "%PROGRAMFILES(X86)%\Java\jre7" ( + IF EXIST "%PROGRAMFILES(X86)%\%JAVAVERSION%" ( "%PROGRAMFILES(X86)%%JAVAEXEC%" %LAUNCH% ) ELSE ( ECHO Java software not found on your system. Please go to http://java.com/en/ to download a copy of Java. From 6b2daba6d1a305220ec9363dee4e50a0896eaaef Mon Sep 17 00:00:00 2001 From: Dennis Fantoni Date: Thu, 17 Sep 2015 19:09:33 +0200 Subject: [PATCH 11/11] forgot tto push latest to github --- compile.bat | 5 +- run_repeat.bat | 2 +- src/java/nxt/Nxt.java | 432 +- src/java/nxt/TransactionProcessorImpl.java | 1074 ++--- src/java/nxt/TransactionType.java | 4820 ++++++++++---------- src/java/nxt/at/AT_API_Impl.java | 1624 +++---- 6 files changed, 3979 insertions(+), 3978 deletions(-) diff --git a/compile.bat b/compile.bat index be1dde344..4b9edb973 100644 --- a/compile.bat +++ b/compile.bat @@ -1,15 +1,16 @@ set CP=conf\;classes\;lib\* set SP=src\java\ +set JRE=jdk1.8.0_40 md classes -"C:\Program Files\Java\jdk1.7.0_67\bin\javac.exe" -sourcepath %SP% -classpath %CP% -d classes\ src\java\nxt\*.java src\java\nxt\crypto\*.java src\java\nxt\util\*.java src\java\nxt\http\*.java src\java\fr\cryptohash\*.java src\java\nxt\at\*.java src\java\nxt\peer\*.java src\java\nxt\user\*.java src\java\nxt\db\*.java src\java\fr\cryptohash\test\*.java +"C:\Program Files\Java\%JRE%\bin\javac.exe" -sourcepath %SP% -classpath %CP% -d classes\ src\java\nxt\*.java src\java\nxt\crypto\*.java src\java\nxt\util\*.java src\java\nxt\http\*.java src\java\fr\cryptohash\*.java src\java\nxt\at\*.java src\java\nxt\peer\*.java src\java\nxt\user\*.java src\java\nxt\db\*.java src\java\fr\cryptohash\test\*.java :/bin/rm -f burst.jar :this seems to be overwritten by the compiler so not really any need to remove it on windows -"C:\Program Files\Java\jdk1.7.0_67\bin\jar.exe" cf burst.jar -C classes . +"C:\Program Files\Java\%JRE%\bin\jar.exe" cf burst.jar -C classes . :/bin/rm -rf classes :seems to update fine even if not removed, so no immediate need to remove the directory diff --git a/run_repeat.bat b/run_repeat.bat index 2f45a4b61..fe8cf62cf 100644 --- a/run_repeat.bat +++ b/run_repeat.bat @@ -1,7 +1,7 @@ title BURST WALLET set MAXRAM=700m set LAUNCH=-Xmx%MAXRAM% -cp burst.jar;lib\*;conf nxt.Nxt -set JAVAVERSION=jre1.8.0_31 +set JAVAVERSION=jre1.8.0_40 set JAVAEXEC=\Java\%JAVAVERSION%\bin\java.exe :runagain @ECHO OFF diff --git a/src/java/nxt/Nxt.java b/src/java/nxt/Nxt.java index 6b1fc3a95..7e3dfbb1f 100644 --- a/src/java/nxt/Nxt.java +++ b/src/java/nxt/Nxt.java @@ -1,216 +1,216 @@ -package nxt; - -import nxt.db.Db; -import nxt.http.API; -import nxt.peer.Peers; -import nxt.user.Users; -import nxt.util.Logger; -import nxt.util.ThreadPool; -import nxt.util.Time; - -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Properties; - -public final class Nxt { - - public static final String VERSION = "1.2.3"; - public static final String APPLICATION = "NRS"; - - private static volatile Time time = new Time.EpochTime(); - - private static final Properties defaultProperties = new Properties(); - static { - System.out.println("Initializing Burst server version " + Nxt.VERSION); - try (InputStream is = ClassLoader.getSystemResourceAsStream("nxt-default.properties")) { - if (is != null) { - Nxt.defaultProperties.load(is); - } else { - String configFile = System.getProperty("nxt-default.properties"); - if (configFile != null) { - try (InputStream fis = new FileInputStream(configFile)) { - Nxt.defaultProperties.load(fis); - } catch (IOException e) { - throw new RuntimeException("Error loading nxt-default.properties from " + configFile); - } - } else { - throw new RuntimeException("nxt-default.properties not in classpath and system property nxt-default.properties not defined either"); - } - } - } catch (IOException e) { - throw new RuntimeException("Error loading nxt-default.properties", e); - } - } - private static final Properties properties = new Properties(defaultProperties); - static { - try (InputStream is = ClassLoader.getSystemResourceAsStream("nxt.properties")) { - if (is != null) { - Nxt.properties.load(is); - } // ignore if missing - } catch (IOException e) { - throw new RuntimeException("Error loading nxt.properties", e); - } - } - - public static int getIntProperty(String name) { - try { - int result = Integer.parseInt(properties.getProperty(name)); - Logger.logMessage(name + " = \"" + result + "\""); - return result; - } catch (NumberFormatException e) { - Logger.logMessage(name + " not defined, assuming 0"); - return 0; - } - } - - public static String getStringProperty(String name) { - return getStringProperty(name, null); - } - - public static String getStringProperty(String name, String defaultValue) { - String value = properties.getProperty(name); - if (value != null && ! "".equals(value)) { - Logger.logMessage(name + " = \"" + value + "\""); - return value; - } else { - Logger.logMessage(name + " not defined"); - return defaultValue; - } - } - - public static List getStringListProperty(String name) { - String value = getStringProperty(name); - if (value == null || value.length() == 0) { - return Collections.emptyList(); - } - List result = new ArrayList<>(); - for (String s : value.split(";")) { - s = s.trim(); - if (s.length() > 0) { - result.add(s); - } - } - return result; - } - - public static Boolean getBooleanProperty(String name) { - String value = properties.getProperty(name); - if (Boolean.TRUE.toString().equals(value)) { - Logger.logMessage(name + " = \"true\""); - return true; - } else if (Boolean.FALSE.toString().equals(value)) { - Logger.logMessage(name + " = \"false\""); - return false; - } - Logger.logMessage(name + " not defined, assuming false"); - return false; - } - - public static Blockchain getBlockchain() { - return BlockchainImpl.getInstance(); - } - - public static BlockchainProcessor getBlockchainProcessor() { - return BlockchainProcessorImpl.getInstance(); - } - - public static TransactionProcessor getTransactionProcessor() { - return TransactionProcessorImpl.getInstance(); - } - - public static int getEpochTime() { - return time.getTime(); - } - - static void setTime(Time time) { - Nxt.time = time; - } - - public static void main(String[] args) { - Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { - @Override - public void run() { - Nxt.shutdown(); - } - })); - init(); - } - - public static void init(Properties customProperties) { - properties.putAll(customProperties); - init(); - } - - public static void init() { - Init.init(); - } - - public static void shutdown() { - Logger.logShutdownMessage("Shutting down..."); - API.shutdown(); - Users.shutdown(); - Peers.shutdown(); - ThreadPool.shutdown(); - Db.shutdown(); - Logger.logShutdownMessage("Burst server " + VERSION + " stopped."); - Logger.shutdown(); - } - - private static class Init { - - static { - try { - long startTime = System.currentTimeMillis(); - Logger.init(); - Db.init(); - TransactionProcessorImpl.getInstance(); - BlockchainProcessorImpl.getInstance(); - DbVersion.init(); - Account.init(); - Alias.init(); - Asset.init(); - DigitalGoodsStore.init(); - Hub.init(); - Order.init(); - Poll.init(); - Trade.init(); - AssetTransfer.init(); - Vote.init(); - AT.init(); - Peers.init(); - Generator.init(); - API.init(); - Users.init(); - DebugTrace.init(); - int timeMultiplier = (Constants.isTestnet && Constants.isOffline) ? Math.max(Nxt.getIntProperty("nxt.timeMultiplier"), 1) : 1; - ThreadPool.start(timeMultiplier); - if (timeMultiplier > 1) { - setTime(new Time.FasterTime(Math.max(getEpochTime(), Nxt.getBlockchain().getLastBlock().getTimestamp()), timeMultiplier)); - Logger.logMessage("TIME WILL FLOW " + timeMultiplier + " TIMES FASTER!"); - } - - long currentTime = System.currentTimeMillis(); - Logger.logMessage("Initialization took " + (currentTime - startTime) / 1000 + " seconds"); - Logger.logMessage("Burst server " + VERSION + " started successfully."); - if (Constants.isTestnet) { - Logger.logMessage("RUNNING ON TESTNET - DO NOT USE REAL ACCOUNTS!"); - } - } catch (Exception e) { - Logger.logErrorMessage(e.getMessage(), e); - System.exit(1); - } - } - - private static void init() {} - - private Init() {} // never - - } - - private Nxt() {} // never - -} +package nxt; + +import nxt.db.Db; +import nxt.http.API; +import nxt.peer.Peers; +import nxt.user.Users; +import nxt.util.Logger; +import nxt.util.ThreadPool; +import nxt.util.Time; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Properties; + +public final class Nxt { + + public static final String VERSION = "1.2.3"; + public static final String APPLICATION = "NRS"; + + private static volatile Time time = new Time.EpochTime(); + + private static final Properties defaultProperties = new Properties(); + static { + System.out.println("Initializing Burst server version " + Nxt.VERSION); + try (InputStream is = ClassLoader.getSystemResourceAsStream("nxt-default.properties")) { + if (is != null) { + Nxt.defaultProperties.load(is); + } else { + String configFile = System.getProperty("nxt-default.properties"); + if (configFile != null) { + try (InputStream fis = new FileInputStream(configFile)) { + Nxt.defaultProperties.load(fis); + } catch (IOException e) { + throw new RuntimeException("Error loading nxt-default.properties from " + configFile); + } + } else { + throw new RuntimeException("nxt-default.properties not in classpath and system property nxt-default.properties not defined either"); + } + } + } catch (IOException e) { + throw new RuntimeException("Error loading nxt-default.properties", e); + } + } + private static final Properties properties = new Properties(defaultProperties); + static { + try (InputStream is = ClassLoader.getSystemResourceAsStream("nxt.properties")) { + if (is != null) { + Nxt.properties.load(is); + } // ignore if missing + } catch (IOException e) { + throw new RuntimeException("Error loading nxt.properties", e); + } + } + + public static int getIntProperty(String name) { + try { + int result = Integer.parseInt(properties.getProperty(name)); + Logger.logMessage(name + " = \"" + result + "\""); + return result; + } catch (NumberFormatException e) { + Logger.logMessage(name + " not defined, assuming 0"); + return 0; + } + } + + public static String getStringProperty(String name) { + return getStringProperty(name, null); + } + + public static String getStringProperty(String name, String defaultValue) { + String value = properties.getProperty(name); + if (value != null && ! "".equals(value)) { + Logger.logMessage(name + " = \"" + value + "\""); + return value; + } else { + Logger.logMessage(name + " not defined"); + return defaultValue; + } + } + + public static List getStringListProperty(String name) { + String value = getStringProperty(name); + if (value == null || value.length() == 0) { + return Collections.emptyList(); + } + List result = new ArrayList<>(); + for (String s : value.split(";")) { + s = s.trim(); + if (s.length() > 0) { + result.add(s); + } + } + return result; + } + + public static Boolean getBooleanProperty(String name) { + String value = properties.getProperty(name); + if (Boolean.TRUE.toString().equals(value)) { + Logger.logMessage(name + " = \"true\""); + return true; + } else if (Boolean.FALSE.toString().equals(value)) { + Logger.logMessage(name + " = \"false\""); + return false; + } + Logger.logMessage(name + " not defined, assuming false"); + return false; + } + + public static Blockchain getBlockchain() { + return BlockchainImpl.getInstance(); + } + + public static BlockchainProcessor getBlockchainProcessor() { + return BlockchainProcessorImpl.getInstance(); + } + + public static TransactionProcessor getTransactionProcessor() { + return TransactionProcessorImpl.getInstance(); + } + + public static int getEpochTime() { + return time.getTime(); + } + + static void setTime(Time time) { + Nxt.time = time; + } + + public static void main(String[] args) { + Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { + @Override + public void run() { + Nxt.shutdown(); + } + })); + init(); + } + + public static void init(Properties customProperties) { + properties.putAll(customProperties); + init(); + } + + public static void init() { + Init.init(); + } + + public static void shutdown() { + Logger.logShutdownMessage("Shutting down..."); + API.shutdown(); + Users.shutdown(); + Peers.shutdown(); + ThreadPool.shutdown(); + Db.shutdown(); + Logger.logShutdownMessage("Burst server " + VERSION + " stopped."); + Logger.shutdown(); + } + + private static class Init { + + static { + try { + long startTime = System.currentTimeMillis(); + Logger.init(); + Db.init(); + TransactionProcessorImpl.getInstance(); + BlockchainProcessorImpl.getInstance(); + DbVersion.init(); + Account.init(); + Alias.init(); + Asset.init(); + DigitalGoodsStore.init(); + Hub.init(); + Order.init(); + Poll.init(); + Trade.init(); + AssetTransfer.init(); + Vote.init(); + AT.init(); + Peers.init(); + Generator.init(); + API.init(); + Users.init(); + DebugTrace.init(); + int timeMultiplier = (Constants.isTestnet && Constants.isOffline) ? Math.max(Nxt.getIntProperty("nxt.timeMultiplier"), 1) : 1; + ThreadPool.start(timeMultiplier); + if (timeMultiplier > 1) { + setTime(new Time.FasterTime(Math.max(getEpochTime(), Nxt.getBlockchain().getLastBlock().getTimestamp()), timeMultiplier)); + Logger.logMessage("TIME WILL FLOW " + timeMultiplier + " TIMES FASTER!"); + } + + long currentTime = System.currentTimeMillis(); + Logger.logMessage("Initialization took " + (currentTime - startTime) / 1000 + " seconds"); + Logger.logMessage("Burst server " + VERSION + " started successfully."); + if (Constants.isTestnet) { + Logger.logMessage("RUNNING ON TESTNET - DO NOT USE REAL ACCOUNTS!"); + } + } catch (Exception e) { + Logger.logErrorMessage(e.getMessage(), e); + System.exit(1); + } + } + + private static void init() {} + + private Init() {} // never + + } + + private Nxt() {} // never + +} diff --git a/src/java/nxt/TransactionProcessorImpl.java b/src/java/nxt/TransactionProcessorImpl.java index db464ad19..458af9a44 100644 --- a/src/java/nxt/TransactionProcessorImpl.java +++ b/src/java/nxt/TransactionProcessorImpl.java @@ -1,537 +1,537 @@ -package nxt; - -import nxt.db.Db; -import nxt.db.DbClause; -import nxt.db.DbIterator; -import nxt.db.DbKey; -import nxt.db.EntityDbTable; -import nxt.peer.Peer; -import nxt.peer.Peers; -import nxt.util.JSON; -import nxt.util.Listener; -import nxt.util.Listeners; -import nxt.util.Logger; -import nxt.util.ThreadPool; -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import org.json.simple.JSONStreamAware; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -final class TransactionProcessorImpl implements TransactionProcessor { - - private static final boolean enableTransactionRebroadcasting = Nxt.getBooleanProperty("nxt.enableTransactionRebroadcasting"); - private static final boolean testUnconfirmedTransactions = Nxt.getBooleanProperty("nxt.testUnconfirmedTransactions"); - - private static final TransactionProcessorImpl instance = new TransactionProcessorImpl(); - - static TransactionProcessorImpl getInstance() { - return instance; - } - - final DbKey.LongKeyFactory unconfirmedTransactionDbKeyFactory = new DbKey.LongKeyFactory("id") { - - @Override - public DbKey newKey(TransactionImpl transaction) { - return transaction.getDbKey(); - } - - }; - - private final EntityDbTable unconfirmedTransactionTable = new EntityDbTable("unconfirmed_transaction", unconfirmedTransactionDbKeyFactory) { - - @Override - protected TransactionImpl load(Connection con, ResultSet rs) throws SQLException { - byte[] transactionBytes = rs.getBytes("transaction_bytes"); - try { - TransactionImpl transaction = TransactionImpl.parseTransaction(transactionBytes); - transaction.setHeight(rs.getInt("transaction_height")); - return transaction; - } catch (NxtException.ValidationException e) { - throw new RuntimeException(e.toString(), e); - } - } - - @Override - protected void save(Connection con, TransactionImpl transaction) throws SQLException { - try (PreparedStatement pstmt = con.prepareStatement("INSERT INTO unconfirmed_transaction (id, transaction_height, " - + "fee_per_byte, timestamp, expiration, transaction_bytes, height) " - + "VALUES (?, ?, ?, ?, ?, ?, ?)")) { - int i = 0; - pstmt.setLong(++i, transaction.getId()); - pstmt.setInt(++i, transaction.getHeight()); - pstmt.setLong(++i, transaction.getFeeNQT() / transaction.getSize()); - pstmt.setInt(++i, transaction.getTimestamp()); - pstmt.setInt(++i, transaction.getExpiration()); - pstmt.setBytes(++i, transaction.getBytes()); - pstmt.setInt(++i, Nxt.getBlockchain().getHeight()); - pstmt.executeUpdate(); - } - } - - @Override - public void rollback(int height) { - List transactions = new ArrayList<>(); - try (Connection con = Db.getConnection(); - PreparedStatement pstmt = con.prepareStatement("SELECT * FROM unconfirmed_transaction WHERE height > ?")) { - pstmt.setInt(1, height); - try (ResultSet rs = pstmt.executeQuery()) { - while (rs.next()) { - transactions.add(load(con, rs)); - } - } - } catch (SQLException e) { - throw new RuntimeException(e.toString(), e); - } - super.rollback(height); - processLater(transactions); - } - - @Override - protected String defaultSort() { - return " ORDER BY transaction_height ASC, fee_per_byte DESC, timestamp ASC, id ASC "; - } - - }; - - private final Set nonBroadcastedTransactions = Collections.newSetFromMap(new ConcurrentHashMap()); - private final Listeners,Event> transactionListeners = new Listeners<>(); - private final Set lostTransactions = new HashSet<>(); - - private final Runnable removeUnconfirmedTransactionsThread = new Runnable() { - - private final DbClause expiredClause = new DbClause(" expiration < ? ") { - @Override - protected int set(PreparedStatement pstmt, int index) throws SQLException { - pstmt.setInt(index, Nxt.getEpochTime()); - return index + 1; - } - }; - - @Override - public void run() { - - try { - try { - List expiredTransactions = new ArrayList<>(); - try (DbIterator iterator = unconfirmedTransactionTable.getManyBy(expiredClause, 0, -1, "")) { - while (iterator.hasNext()) { - expiredTransactions.add(iterator.next()); - } - } - if (expiredTransactions.size() > 0) { - synchronized (BlockchainImpl.getInstance()) { - try { - Db.beginTransaction(); - for (TransactionImpl transaction : expiredTransactions) { - removeUnconfirmedTransaction(transaction); - } - Db.commitTransaction(); - } catch (Exception e) { - Logger.logErrorMessage(e.toString(), e); - Db.rollbackTransaction(); - throw e; - } finally { - Db.endTransaction(); - } - } // synchronized - } - } catch (Exception e) { - Logger.logDebugMessage("Error removing unconfirmed transactions", e); - } - } catch (Throwable t) { - Logger.logMessage("CRITICAL ERROR. PLEASE REPORT TO THE DEVELOPERS.\n" + t.toString()); - t.printStackTrace(); - System.exit(1); - } - - } - - }; - - private final Runnable rebroadcastTransactionsThread = new Runnable() { - - @Override - public void run() { - - try { - try { - List transactionList = new ArrayList<>(); - int curTime = Nxt.getEpochTime(); - for (TransactionImpl transaction : nonBroadcastedTransactions) { - if (TransactionDb.hasTransaction(transaction.getId()) || transaction.getExpiration() < curTime) { - nonBroadcastedTransactions.remove(transaction); - } else if (transaction.getTimestamp() < curTime - 30) { - transactionList.add(transaction); - } - } - - if (transactionList.size() > 0) { - Peers.sendToSomePeers(transactionList); - } - - } catch (Exception e) { - Logger.logDebugMessage("Error in transaction re-broadcasting thread", e); - } - } catch (Throwable t) { - Logger.logMessage("CRITICAL ERROR. PLEASE REPORT TO THE DEVELOPERS.\n" + t.toString()); - t.printStackTrace(); - System.exit(1); - } - - } - - }; - - private final Runnable processTransactionsThread = new Runnable() { - - private final JSONStreamAware getUnconfirmedTransactionsRequest; - { - JSONObject request = new JSONObject(); - request.put("requestType", "getUnconfirmedTransactions"); - getUnconfirmedTransactionsRequest = JSON.prepareRequest(request); - } - - @Override - public void run() { - try { - try { - synchronized (BlockchainImpl.getInstance()) { - processTransactions(lostTransactions, false); - lostTransactions.clear(); - } - Peer peer = Peers.getAnyPeer(Peer.State.CONNECTED, true); - if (peer == null) { - return; - } - JSONObject response = peer.send(getUnconfirmedTransactionsRequest); - if (response == null) { - return; - } - JSONArray transactionsData = (JSONArray)response.get("unconfirmedTransactions"); - if (transactionsData == null || transactionsData.size() == 0) { - return; - } - try { - processPeerTransactions(transactionsData); - } catch (NxtException.ValidationException|RuntimeException e) { - peer.blacklist(e); - } - } catch (Exception e) { - Logger.logDebugMessage("Error processing unconfirmed transactions", e); - } - } catch (Throwable t) { - Logger.logMessage("CRITICAL ERROR. PLEASE REPORT TO THE DEVELOPERS.\n" + t.toString()); - t.printStackTrace(); - System.exit(1); - } - } - - }; - - private TransactionProcessorImpl() { - ThreadPool.scheduleThread("ProcessTransactions", processTransactionsThread, 5); - ThreadPool.scheduleThread("RemoveUnconfirmedTransactions", removeUnconfirmedTransactionsThread, 1); - if (enableTransactionRebroadcasting) { - ThreadPool.scheduleThread("RebroadcastTransactions", rebroadcastTransactionsThread, 60); - ThreadPool.runAfterStart(new Runnable() { - @Override - public void run() { - try (DbIterator oldNonBroadcastedTransactions = getAllUnconfirmedTransactions()) { - for (TransactionImpl transaction : oldNonBroadcastedTransactions) { - nonBroadcastedTransactions.add(transaction); - } - } - } - }); - } - } - - @Override - public boolean addListener(Listener> listener, Event eventType) { - return transactionListeners.addListener(listener, eventType); - } - - @Override - public boolean removeListener(Listener> listener, Event eventType) { - return transactionListeners.removeListener(listener, eventType); - } - - void notifyListeners(List transactions, Event eventType) { - transactionListeners.notify(transactions, eventType); - } - - @Override - public DbIterator getAllUnconfirmedTransactions() { - return unconfirmedTransactionTable.getAll(0, -1); - } - - @Override - public Transaction getUnconfirmedTransaction(long transactionId) { - return unconfirmedTransactionTable.get(unconfirmedTransactionDbKeyFactory.newKey(transactionId)); - } - - public Transaction.Builder newTransactionBuilder(byte[] senderPublicKey, long amountNQT, long feeNQT, short deadline, - Attachment attachment) { - byte version = (byte) getTransactionVersion(Nxt.getBlockchain().getHeight()); - int timestamp = Nxt.getEpochTime(); - TransactionImpl.BuilderImpl builder = new TransactionImpl.BuilderImpl(version, senderPublicKey, amountNQT, feeNQT, timestamp, - deadline, (Attachment.AbstractAttachment)attachment); - if (version > 0) { - Block ecBlock = EconomicClustering.getECBlock(timestamp); - builder.ecBlockHeight(ecBlock.getHeight()); - builder.ecBlockId(ecBlock.getId()); - } - return builder; - } - - @Override - public void broadcast(Transaction transaction) throws NxtException.ValidationException { - if (! transaction.verifySignature()) { - throw new NxtException.NotValidException("Transaction signature verification failed"); - } - List processedTransactions; - synchronized (BlockchainImpl.getInstance()) { - if (TransactionDb.hasTransaction(transaction.getId())) { - Logger.logMessage("Transaction " + transaction.getStringId() + " already in blockchain, will not broadcast again"); - return; - } - if (unconfirmedTransactionTable.get(((TransactionImpl) transaction).getDbKey()) != null) { - if (enableTransactionRebroadcasting) { - nonBroadcastedTransactions.add((TransactionImpl) transaction); - Logger.logMessage("Transaction " + transaction.getStringId() + " already in unconfirmed pool, will re-broadcast"); - } else { - Logger.logMessage("Transaction " + transaction.getStringId() + " already in unconfirmed pool, will not broadcast again"); - } - return; - } - processedTransactions = processTransactions(Collections.singleton((TransactionImpl) transaction), true); - } - if (processedTransactions.contains(transaction)) { - if (enableTransactionRebroadcasting) { - nonBroadcastedTransactions.add((TransactionImpl) transaction); - } - Logger.logDebugMessage("Accepted new transaction " + transaction.getStringId()); - } else { - Logger.logDebugMessage("Could not accept new transaction " + transaction.getStringId()); - throw new NxtException.NotValidException("Invalid transaction " + transaction.getStringId()); - } - } - - @Override - public void processPeerTransactions(JSONObject request) throws NxtException.ValidationException { - JSONArray transactionsData = (JSONArray)request.get("transactions"); - processPeerTransactions(transactionsData); - } - - @Override - public Transaction parseTransaction(byte[] bytes) throws NxtException.ValidationException { - return TransactionImpl.parseTransaction(bytes); - } - - @Override - public TransactionImpl parseTransaction(JSONObject transactionData) throws NxtException.NotValidException { - return TransactionImpl.parseTransaction(transactionData); - } - - @Override - public void clearUnconfirmedTransactions() { - synchronized (BlockchainImpl.getInstance()) { - List removed = new ArrayList<>(); - try { - Db.beginTransaction(); - try (DbIterator unconfirmedTransactions = getAllUnconfirmedTransactions()) { - for (TransactionImpl transaction : unconfirmedTransactions) { - transaction.undoUnconfirmed(); - removed.add(transaction); - } - } - unconfirmedTransactionTable.truncate(); - Db.commitTransaction(); - } catch (Exception e) { - Logger.logErrorMessage(e.toString(), e); - Db.rollbackTransaction(); - throw e; - } finally { - Db.endTransaction(); - } - lostTransactions.clear(); - transactionListeners.notify(removed, Event.REMOVED_UNCONFIRMED_TRANSACTIONS); - } - } - - void requeueAllUnconfirmedTransactions() { - List removed = new ArrayList<>(); - try (DbIterator unconfirmedTransactions = getAllUnconfirmedTransactions()) { - for (TransactionImpl transaction : unconfirmedTransactions) { - transaction.undoUnconfirmed(); - removed.add(transaction); - lostTransactions.add(transaction); - } - } - unconfirmedTransactionTable.truncate(); - transactionListeners.notify(removed, Event.REMOVED_UNCONFIRMED_TRANSACTIONS); - } - - void removeUnconfirmedTransaction(TransactionImpl transaction) { - if (!Db.isInTransaction()) { - try { - Db.beginTransaction(); - removeUnconfirmedTransaction(transaction); - Db.commitTransaction(); - } catch (Exception e) { - Logger.logErrorMessage(e.toString(), e); - Db.rollbackTransaction(); - throw e; - } finally { - Db.endTransaction(); - } - return; - } - try (Connection con = Db.getConnection(); - PreparedStatement pstmt = con.prepareStatement("DELETE FROM unconfirmed_transaction WHERE id = ?")) { - pstmt.setLong(1, transaction.getId()); - int deleted = pstmt.executeUpdate(); - if (deleted > 0) { - transaction.undoUnconfirmed(); - transactionListeners.notify(Collections.singletonList(transaction), Event.REMOVED_UNCONFIRMED_TRANSACTIONS); - } - } catch (SQLException e) { - Logger.logErrorMessage(e.toString(), e); - throw new RuntimeException(e.toString(), e); - } - } - - int getTransactionVersion(int previousBlockHeight) { - return previousBlockHeight < Constants.DIGITAL_GOODS_STORE_BLOCK ? 0 : 1; - } - - void processLater(Collection transactions) { - synchronized (BlockchainImpl.getInstance()) { - for (TransactionImpl transaction : transactions) { - lostTransactions.add(transaction); - } - } - } - - private void processPeerTransactions(JSONArray transactionsData) throws NxtException.ValidationException { - if (Nxt.getBlockchain().getLastBlock().getTimestamp() < Nxt.getEpochTime() - 60 * 1440 && ! testUnconfirmedTransactions) { - return; - } - if (Nxt.getBlockchain().getHeight() <= Constants.NQT_BLOCK) { - return; - } - List transactions = new ArrayList<>(); - for (Object transactionData : transactionsData) { - try { - TransactionImpl transaction = parseTransaction((JSONObject) transactionData); - transaction.validate(); - if(!EconomicClustering.verifyFork(transaction)) { - /*if(Nxt.getBlockchain().getHeight() >= Constants.EC_CHANGE_BLOCK_1) { - throw new NxtException.NotValidException("Transaction from wrong fork"); - }*/ - continue; - } - transactions.add(transaction); - } catch (NxtException.NotCurrentlyValidException ignore) { - } catch (NxtException.NotValidException e) { - Logger.logDebugMessage("Invalid transaction from peer: " + ((JSONObject) transactionData).toJSONString()); - throw e; - } - } - processTransactions(transactions, true); - nonBroadcastedTransactions.removeAll(transactions); - } - - List processTransactions(Collection transactions, final boolean sendToPeers) { - if (transactions.isEmpty()) { - return Collections.emptyList(); - } - List sendToPeersTransactions = new ArrayList<>(); - List addedUnconfirmedTransactions = new ArrayList<>(); - List addedDoubleSpendingTransactions = new ArrayList<>(); - - for (TransactionImpl transaction : transactions) { - - try { - - int curTime = Nxt.getEpochTime(); - if (transaction.getTimestamp() > curTime + 15 || transaction.getExpiration() < curTime - || transaction.getDeadline() > 1440) { - continue; - } - //if (transaction.getVersion() < 1) { - // continue; - //} - - synchronized (BlockchainImpl.getInstance()) { - try { - Db.beginTransaction(); - if (Nxt.getBlockchain().getHeight() < Constants.NQT_BLOCK) { - break; // not ready to process transactions - } - - if (TransactionDb.hasTransaction(transaction.getId()) || unconfirmedTransactionTable.get(transaction.getDbKey()) != null) { - continue; - } - - if (! transaction.verifySignature()) { - if (Account.getAccount(transaction.getSenderId()) != null) { - Logger.logDebugMessage("Transaction " + transaction.getJSONObject().toJSONString() + " failed to verify"); - } - continue; - } - - if (transaction.applyUnconfirmed()) { - if (sendToPeers) { - if (nonBroadcastedTransactions.contains(transaction)) { - Logger.logDebugMessage("Received back transaction " + transaction.getStringId() - + " that we generated, will not forward to peers"); - nonBroadcastedTransactions.remove(transaction); - } else { - sendToPeersTransactions.add(transaction); - } - } - unconfirmedTransactionTable.insert(transaction); - addedUnconfirmedTransactions.add(transaction); - } else { - addedDoubleSpendingTransactions.add(transaction); - } - Db.commitTransaction(); - } catch (Exception e) { - Db.rollbackTransaction(); - throw e; - } finally { - Db.endTransaction(); - } - } - } catch (RuntimeException e) { - Logger.logMessage("Error processing transaction", e); - } - - } - - if (sendToPeersTransactions.size() > 0) { - Peers.sendToSomePeers(sendToPeersTransactions); - } - - if (addedUnconfirmedTransactions.size() > 0) { - transactionListeners.notify(addedUnconfirmedTransactions, Event.ADDED_UNCONFIRMED_TRANSACTIONS); - } - if (addedDoubleSpendingTransactions.size() > 0) { - transactionListeners.notify(addedDoubleSpendingTransactions, Event.ADDED_DOUBLESPENDING_TRANSACTIONS); - } - return addedUnconfirmedTransactions; - } - -} +package nxt; + +import nxt.db.Db; +import nxt.db.DbClause; +import nxt.db.DbIterator; +import nxt.db.DbKey; +import nxt.db.EntityDbTable; +import nxt.peer.Peer; +import nxt.peer.Peers; +import nxt.util.JSON; +import nxt.util.Listener; +import nxt.util.Listeners; +import nxt.util.Logger; +import nxt.util.ThreadPool; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.JSONStreamAware; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +final class TransactionProcessorImpl implements TransactionProcessor { + + private static final boolean enableTransactionRebroadcasting = Nxt.getBooleanProperty("nxt.enableTransactionRebroadcasting"); + private static final boolean testUnconfirmedTransactions = Nxt.getBooleanProperty("nxt.testUnconfirmedTransactions"); + + private static final TransactionProcessorImpl instance = new TransactionProcessorImpl(); + + static TransactionProcessorImpl getInstance() { + return instance; + } + + final DbKey.LongKeyFactory unconfirmedTransactionDbKeyFactory = new DbKey.LongKeyFactory("id") { + + @Override + public DbKey newKey(TransactionImpl transaction) { + return transaction.getDbKey(); + } + + }; + + private final EntityDbTable unconfirmedTransactionTable = new EntityDbTable("unconfirmed_transaction", unconfirmedTransactionDbKeyFactory) { + + @Override + protected TransactionImpl load(Connection con, ResultSet rs) throws SQLException { + byte[] transactionBytes = rs.getBytes("transaction_bytes"); + try { + TransactionImpl transaction = TransactionImpl.parseTransaction(transactionBytes); + transaction.setHeight(rs.getInt("transaction_height")); + return transaction; + } catch (NxtException.ValidationException e) { + throw new RuntimeException(e.toString(), e); + } + } + + @Override + protected void save(Connection con, TransactionImpl transaction) throws SQLException { + try (PreparedStatement pstmt = con.prepareStatement("INSERT INTO unconfirmed_transaction (id, transaction_height, " + + "fee_per_byte, timestamp, expiration, transaction_bytes, height) " + + "VALUES (?, ?, ?, ?, ?, ?, ?)")) { + int i = 0; + pstmt.setLong(++i, transaction.getId()); + pstmt.setInt(++i, transaction.getHeight()); + pstmt.setLong(++i, transaction.getFeeNQT() / transaction.getSize()); + pstmt.setInt(++i, transaction.getTimestamp()); + pstmt.setInt(++i, transaction.getExpiration()); + pstmt.setBytes(++i, transaction.getBytes()); + pstmt.setInt(++i, Nxt.getBlockchain().getHeight()); + pstmt.executeUpdate(); + } + } + + @Override + public void rollback(int height) { + List transactions = new ArrayList<>(); + try (Connection con = Db.getConnection(); + PreparedStatement pstmt = con.prepareStatement("SELECT * FROM unconfirmed_transaction WHERE height > ?")) { + pstmt.setInt(1, height); + try (ResultSet rs = pstmt.executeQuery()) { + while (rs.next()) { + transactions.add(load(con, rs)); + } + } + } catch (SQLException e) { + throw new RuntimeException(e.toString(), e); + } + super.rollback(height); + processLater(transactions); + } + + @Override + protected String defaultSort() { + return " ORDER BY transaction_height ASC, fee_per_byte DESC, timestamp ASC, id ASC "; + } + + }; + + private final Set nonBroadcastedTransactions = Collections.newSetFromMap(new ConcurrentHashMap()); + private final Listeners,Event> transactionListeners = new Listeners<>(); + private final Set lostTransactions = new HashSet<>(); + + private final Runnable removeUnconfirmedTransactionsThread = new Runnable() { + + private final DbClause expiredClause = new DbClause(" expiration < ? ") { + @Override + protected int set(PreparedStatement pstmt, int index) throws SQLException { + pstmt.setInt(index, Nxt.getEpochTime()); + return index + 1; + } + }; + + @Override + public void run() { + + try { + try { + List expiredTransactions = new ArrayList<>(); + try (DbIterator iterator = unconfirmedTransactionTable.getManyBy(expiredClause, 0, -1, "")) { + while (iterator.hasNext()) { + expiredTransactions.add(iterator.next()); + } + } + if (expiredTransactions.size() > 0) { + synchronized (BlockchainImpl.getInstance()) { + try { + Db.beginTransaction(); + for (TransactionImpl transaction : expiredTransactions) { + removeUnconfirmedTransaction(transaction); + } + Db.commitTransaction(); + } catch (Exception e) { + Logger.logErrorMessage(e.toString(), e); + Db.rollbackTransaction(); + throw e; + } finally { + Db.endTransaction(); + } + } // synchronized + } + } catch (Exception e) { + Logger.logDebugMessage("Error removing unconfirmed transactions", e); + } + } catch (Throwable t) { + Logger.logMessage("CRITICAL ERROR. PLEASE REPORT TO THE DEVELOPERS.\n" + t.toString()); + t.printStackTrace(); + System.exit(1); + } + + } + + }; + + private final Runnable rebroadcastTransactionsThread = new Runnable() { + + @Override + public void run() { + + try { + try { + List transactionList = new ArrayList<>(); + int curTime = Nxt.getEpochTime(); + for (TransactionImpl transaction : nonBroadcastedTransactions) { + if (TransactionDb.hasTransaction(transaction.getId()) || transaction.getExpiration() < curTime) { + nonBroadcastedTransactions.remove(transaction); + } else if (transaction.getTimestamp() < curTime - 30) { + transactionList.add(transaction); + } + } + + if (transactionList.size() > 0) { + Peers.sendToSomePeers(transactionList); + } + + } catch (Exception e) { + Logger.logDebugMessage("Error in transaction re-broadcasting thread", e); + } + } catch (Throwable t) { + Logger.logMessage("CRITICAL ERROR. PLEASE REPORT TO THE DEVELOPERS.\n" + t.toString()); + t.printStackTrace(); + System.exit(1); + } + + } + + }; + + private final Runnable processTransactionsThread = new Runnable() { + + private final JSONStreamAware getUnconfirmedTransactionsRequest; + { + JSONObject request = new JSONObject(); + request.put("requestType", "getUnconfirmedTransactions"); + getUnconfirmedTransactionsRequest = JSON.prepareRequest(request); + } + + @Override + public void run() { + try { + try { + synchronized (BlockchainImpl.getInstance()) { + processTransactions(lostTransactions, false); + lostTransactions.clear(); + } + Peer peer = Peers.getAnyPeer(Peer.State.CONNECTED, true); + if (peer == null) { + return; + } + JSONObject response = peer.send(getUnconfirmedTransactionsRequest); + if (response == null) { + return; + } + JSONArray transactionsData = (JSONArray)response.get("unconfirmedTransactions"); + if (transactionsData == null || transactionsData.size() == 0) { + return; + } + try { + processPeerTransactions(transactionsData); + } catch (NxtException.ValidationException|RuntimeException e) { + peer.blacklist(e); + } + } catch (Exception e) { + Logger.logDebugMessage("Error processing unconfirmed transactions", e); + } + } catch (Throwable t) { + Logger.logMessage("CRITICAL ERROR. PLEASE REPORT TO THE DEVELOPERS.\n" + t.toString()); + t.printStackTrace(); + System.exit(1); + } + } + + }; + + private TransactionProcessorImpl() { + ThreadPool.scheduleThread("ProcessTransactions", processTransactionsThread, 5); + ThreadPool.scheduleThread("RemoveUnconfirmedTransactions", removeUnconfirmedTransactionsThread, 1); + if (enableTransactionRebroadcasting) { + ThreadPool.scheduleThread("RebroadcastTransactions", rebroadcastTransactionsThread, 60); + ThreadPool.runAfterStart(new Runnable() { + @Override + public void run() { + try (DbIterator oldNonBroadcastedTransactions = getAllUnconfirmedTransactions()) { + for (TransactionImpl transaction : oldNonBroadcastedTransactions) { + nonBroadcastedTransactions.add(transaction); + } + } + } + }); + } + } + + @Override + public boolean addListener(Listener> listener, Event eventType) { + return transactionListeners.addListener(listener, eventType); + } + + @Override + public boolean removeListener(Listener> listener, Event eventType) { + return transactionListeners.removeListener(listener, eventType); + } + + void notifyListeners(List transactions, Event eventType) { + transactionListeners.notify(transactions, eventType); + } + + @Override + public DbIterator getAllUnconfirmedTransactions() { + return unconfirmedTransactionTable.getAll(0, -1); + } + + @Override + public Transaction getUnconfirmedTransaction(long transactionId) { + return unconfirmedTransactionTable.get(unconfirmedTransactionDbKeyFactory.newKey(transactionId)); + } + + public Transaction.Builder newTransactionBuilder(byte[] senderPublicKey, long amountNQT, long feeNQT, short deadline, + Attachment attachment) { + byte version = (byte) getTransactionVersion(Nxt.getBlockchain().getHeight()); + int timestamp = Nxt.getEpochTime(); + TransactionImpl.BuilderImpl builder = new TransactionImpl.BuilderImpl(version, senderPublicKey, amountNQT, feeNQT, timestamp, + deadline, (Attachment.AbstractAttachment)attachment); + if (version > 0) { + Block ecBlock = EconomicClustering.getECBlock(timestamp); + builder.ecBlockHeight(ecBlock.getHeight()); + builder.ecBlockId(ecBlock.getId()); + } + return builder; + } + + @Override + public void broadcast(Transaction transaction) throws NxtException.ValidationException { + if (! transaction.verifySignature()) { + throw new NxtException.NotValidException("Transaction signature verification failed"); + } + List processedTransactions; + synchronized (BlockchainImpl.getInstance()) { + if (TransactionDb.hasTransaction(transaction.getId())) { + Logger.logMessage("Transaction " + transaction.getStringId() + " already in blockchain, will not broadcast again"); + return; + } + if (unconfirmedTransactionTable.get(((TransactionImpl) transaction).getDbKey()) != null) { + if (enableTransactionRebroadcasting) { + nonBroadcastedTransactions.add((TransactionImpl) transaction); + Logger.logMessage("Transaction " + transaction.getStringId() + " already in unconfirmed pool, will re-broadcast"); + } else { + Logger.logMessage("Transaction " + transaction.getStringId() + " already in unconfirmed pool, will not broadcast again"); + } + return; + } + processedTransactions = processTransactions(Collections.singleton((TransactionImpl) transaction), true); + } + if (processedTransactions.contains(transaction)) { + if (enableTransactionRebroadcasting) { + nonBroadcastedTransactions.add((TransactionImpl) transaction); + } + Logger.logDebugMessage("Accepted new transaction " + transaction.getStringId()); + } else { + Logger.logDebugMessage("Could not accept new transaction " + transaction.getStringId()); + throw new NxtException.NotValidException("Invalid transaction " + transaction.getStringId()); + } + } + + @Override + public void processPeerTransactions(JSONObject request) throws NxtException.ValidationException { + JSONArray transactionsData = (JSONArray)request.get("transactions"); + processPeerTransactions(transactionsData); + } + + @Override + public Transaction parseTransaction(byte[] bytes) throws NxtException.ValidationException { + return TransactionImpl.parseTransaction(bytes); + } + + @Override + public TransactionImpl parseTransaction(JSONObject transactionData) throws NxtException.NotValidException { + return TransactionImpl.parseTransaction(transactionData); + } + + @Override + public void clearUnconfirmedTransactions() { + synchronized (BlockchainImpl.getInstance()) { + List removed = new ArrayList<>(); + try { + Db.beginTransaction(); + try (DbIterator unconfirmedTransactions = getAllUnconfirmedTransactions()) { + for (TransactionImpl transaction : unconfirmedTransactions) { + transaction.undoUnconfirmed(); + removed.add(transaction); + } + } + unconfirmedTransactionTable.truncate(); + Db.commitTransaction(); + } catch (Exception e) { + Logger.logErrorMessage(e.toString(), e); + Db.rollbackTransaction(); + throw e; + } finally { + Db.endTransaction(); + } + lostTransactions.clear(); + transactionListeners.notify(removed, Event.REMOVED_UNCONFIRMED_TRANSACTIONS); + } + } + + void requeueAllUnconfirmedTransactions() { + List removed = new ArrayList<>(); + try (DbIterator unconfirmedTransactions = getAllUnconfirmedTransactions()) { + for (TransactionImpl transaction : unconfirmedTransactions) { + transaction.undoUnconfirmed(); + removed.add(transaction); + lostTransactions.add(transaction); + } + } + unconfirmedTransactionTable.truncate(); + transactionListeners.notify(removed, Event.REMOVED_UNCONFIRMED_TRANSACTIONS); + } + + void removeUnconfirmedTransaction(TransactionImpl transaction) { + if (!Db.isInTransaction()) { + try { + Db.beginTransaction(); + removeUnconfirmedTransaction(transaction); + Db.commitTransaction(); + } catch (Exception e) { + Logger.logErrorMessage(e.toString(), e); + Db.rollbackTransaction(); + throw e; + } finally { + Db.endTransaction(); + } + return; + } + try (Connection con = Db.getConnection(); + PreparedStatement pstmt = con.prepareStatement("DELETE FROM unconfirmed_transaction WHERE id = ?")) { + pstmt.setLong(1, transaction.getId()); + int deleted = pstmt.executeUpdate(); + if (deleted > 0) { + transaction.undoUnconfirmed(); + transactionListeners.notify(Collections.singletonList(transaction), Event.REMOVED_UNCONFIRMED_TRANSACTIONS); + } + } catch (SQLException e) { + Logger.logErrorMessage(e.toString(), e); + throw new RuntimeException(e.toString(), e); + } + } + + int getTransactionVersion(int previousBlockHeight) { + return previousBlockHeight < Constants.DIGITAL_GOODS_STORE_BLOCK ? 0 : 1; + } + + void processLater(Collection transactions) { + synchronized (BlockchainImpl.getInstance()) { + for (TransactionImpl transaction : transactions) { + lostTransactions.add(transaction); + } + } + } + + private void processPeerTransactions(JSONArray transactionsData) throws NxtException.ValidationException { + if (Nxt.getBlockchain().getLastBlock().getTimestamp() < Nxt.getEpochTime() - 60 * 1440 && ! testUnconfirmedTransactions) { + return; + } + if (Nxt.getBlockchain().getHeight() <= Constants.NQT_BLOCK) { + return; + } + List transactions = new ArrayList<>(); + for (Object transactionData : transactionsData) { + try { + TransactionImpl transaction = parseTransaction((JSONObject) transactionData); + transaction.validate(); + if(!EconomicClustering.verifyFork(transaction)) { + /*if(Nxt.getBlockchain().getHeight() >= Constants.EC_CHANGE_BLOCK_1) { + throw new NxtException.NotValidException("Transaction from wrong fork"); + }*/ + continue; + } + transactions.add(transaction); + } catch (NxtException.NotCurrentlyValidException ignore) { + } catch (NxtException.NotValidException e) { + Logger.logDebugMessage("Invalid transaction from peer: " + ((JSONObject) transactionData).toJSONString()); + throw e; + } + } + processTransactions(transactions, true); + nonBroadcastedTransactions.removeAll(transactions); + } + + List processTransactions(Collection transactions, final boolean sendToPeers) { + if (transactions.isEmpty()) { + return Collections.emptyList(); + } + List sendToPeersTransactions = new ArrayList<>(); + List addedUnconfirmedTransactions = new ArrayList<>(); + List addedDoubleSpendingTransactions = new ArrayList<>(); + + for (TransactionImpl transaction : transactions) { + + try { + + int curTime = Nxt.getEpochTime(); + if (transaction.getTimestamp() > curTime + 15 || transaction.getExpiration() < curTime + || transaction.getDeadline() > 1440) { + continue; + } + //if (transaction.getVersion() < 1) { + // continue; + //} + + synchronized (BlockchainImpl.getInstance()) { + try { + Db.beginTransaction(); + if (Nxt.getBlockchain().getHeight() < Constants.NQT_BLOCK) { + break; // not ready to process transactions + } + + if (TransactionDb.hasTransaction(transaction.getId()) || unconfirmedTransactionTable.get(transaction.getDbKey()) != null) { + continue; + } + + if (! transaction.verifySignature()) { + if (Account.getAccount(transaction.getSenderId()) != null) { + Logger.logDebugMessage("Transaction " + transaction.getJSONObject().toJSONString() + " failed to verify"); + } + continue; + } + + if (transaction.applyUnconfirmed()) { + if (sendToPeers) { + if (nonBroadcastedTransactions.contains(transaction)) { + Logger.logDebugMessage("Received back transaction " + transaction.getStringId() + + " that we generated, will not forward to peers"); + nonBroadcastedTransactions.remove(transaction); + } else { + sendToPeersTransactions.add(transaction); + } + } + unconfirmedTransactionTable.insert(transaction); + addedUnconfirmedTransactions.add(transaction); + } else { + addedDoubleSpendingTransactions.add(transaction); + } + Db.commitTransaction(); + } catch (Exception e) { + Db.rollbackTransaction(); + throw e; + } finally { + Db.endTransaction(); + } + } + } catch (RuntimeException e) { + Logger.logMessage("Error processing transaction", e); + } + + } + + if (sendToPeersTransactions.size() > 0) { + Peers.sendToSomePeers(sendToPeersTransactions); + } + + if (addedUnconfirmedTransactions.size() > 0) { + transactionListeners.notify(addedUnconfirmedTransactions, Event.ADDED_UNCONFIRMED_TRANSACTIONS); + } + if (addedDoubleSpendingTransactions.size() > 0) { + transactionListeners.notify(addedDoubleSpendingTransactions, Event.ADDED_DOUBLESPENDING_TRANSACTIONS); + } + return addedUnconfirmedTransactions; + } + +} diff --git a/src/java/nxt/TransactionType.java b/src/java/nxt/TransactionType.java index bab43f6de..49edf9731 100644 --- a/src/java/nxt/TransactionType.java +++ b/src/java/nxt/TransactionType.java @@ -1,2410 +1,2410 @@ -package nxt; - -import nxt.Attachment.AbstractAttachment; -import nxt.Attachment.AutomatedTransactionsCreation; -import nxt.NxtException.NotValidException; -import nxt.NxtException.ValidationException; -import nxt.at.AT_Constants; -import nxt.at.AT_Controller; -import nxt.at.AT_Exception; -import nxt.util.Convert; - -import org.json.simple.JSONObject; - -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - - -public abstract class TransactionType { - - private static final byte TYPE_PAYMENT = 0; - private static final byte TYPE_MESSAGING = 1; - private static final byte TYPE_COLORED_COINS = 2; - private static final byte TYPE_DIGITAL_GOODS = 3; - private static final byte TYPE_ACCOUNT_CONTROL = 4; - - private static final byte TYPE_BURST_MINING = 20; // jump some for easier nxt updating - private static final byte TYPE_ADVANCED_PAYMENT = 21; - private static final byte TYPE_AUTOMATED_TRANSACTIONS = 22; - - private static final byte SUBTYPE_PAYMENT_ORDINARY_PAYMENT = 0; - - private static final byte SUBTYPE_MESSAGING_ARBITRARY_MESSAGE = 0; - private static final byte SUBTYPE_MESSAGING_ALIAS_ASSIGNMENT = 1; - private static final byte SUBTYPE_MESSAGING_POLL_CREATION = 2; - private static final byte SUBTYPE_MESSAGING_VOTE_CASTING = 3; - private static final byte SUBTYPE_MESSAGING_HUB_ANNOUNCEMENT = 4; - private static final byte SUBTYPE_MESSAGING_ACCOUNT_INFO = 5; - private static final byte SUBTYPE_MESSAGING_ALIAS_SELL = 6; - private static final byte SUBTYPE_MESSAGING_ALIAS_BUY = 7; - - private static final byte SUBTYPE_COLORED_COINS_ASSET_ISSUANCE = 0; - private static final byte SUBTYPE_COLORED_COINS_ASSET_TRANSFER = 1; - private static final byte SUBTYPE_COLORED_COINS_ASK_ORDER_PLACEMENT = 2; - private static final byte SUBTYPE_COLORED_COINS_BID_ORDER_PLACEMENT = 3; - private static final byte SUBTYPE_COLORED_COINS_ASK_ORDER_CANCELLATION = 4; - private static final byte SUBTYPE_COLORED_COINS_BID_ORDER_CANCELLATION = 5; - - private static final byte SUBTYPE_DIGITAL_GOODS_LISTING = 0; - private static final byte SUBTYPE_DIGITAL_GOODS_DELISTING = 1; - private static final byte SUBTYPE_DIGITAL_GOODS_PRICE_CHANGE = 2; - private static final byte SUBTYPE_DIGITAL_GOODS_QUANTITY_CHANGE = 3; - private static final byte SUBTYPE_DIGITAL_GOODS_PURCHASE = 4; - private static final byte SUBTYPE_DIGITAL_GOODS_DELIVERY = 5; - private static final byte SUBTYPE_DIGITAL_GOODS_FEEDBACK = 6; - private static final byte SUBTYPE_DIGITAL_GOODS_REFUND = 7; - - private static final byte SUBTYPE_AT_CREATION = 0; - private static final byte SUBTYPE_AT_NXT_PAYMENT = 1; - - private static final byte SUBTYPE_ACCOUNT_CONTROL_EFFECTIVE_BALANCE_LEASING = 0; - - private static final byte SUBTYPE_BURST_MINING_REWARD_RECIPIENT_ASSIGNMENT = 0; - - private static final byte SUBTYPE_ADVANCED_PAYMENT_ESCROW_CREATION = 0; - private static final byte SUBTYPE_ADVANCED_PAYMENT_ESCROW_SIGN = 1; - private static final byte SUBTYPE_ADVANCED_PAYMENT_ESCROW_RESULT = 2; - private static final byte SUBTYPE_ADVANCED_PAYMENT_SUBSCRIPTION_SUBSCRIBE = 3; - private static final byte SUBTYPE_ADVANCED_PAYMENT_SUBSCRIPTION_CANCEL = 4; - private static final byte SUBTYPE_ADVANCED_PAYMENT_SUBSCRIPTION_PAYMENT = 5; - - private static final int BASELINE_FEE_HEIGHT = 1; // At release time must be less than current block - 1440 - private static final Fee BASELINE_FEE = new Fee(Constants.ONE_NXT, 0); - private static final Fee BASELINE_ASSET_ISSUANCE_FEE = new Fee(1000 * Constants.ONE_NXT, 0); - private static final int NEXT_FEE_HEIGHT = Integer.MAX_VALUE; - private static final Fee NEXT_FEE = new Fee(Constants.ONE_NXT, 0); - private static final Fee NEXT_ASSET_ISSUANCE_FEE = new Fee(1000 * Constants.ONE_NXT, 0); - - public static TransactionType findTransactionType(byte type, byte subtype) { - switch (type) { - case TYPE_PAYMENT: - switch (subtype) { - case SUBTYPE_PAYMENT_ORDINARY_PAYMENT: - return Payment.ORDINARY; - default: - return null; - } - case TYPE_MESSAGING: - switch (subtype) { - case SUBTYPE_MESSAGING_ARBITRARY_MESSAGE: - return Messaging.ARBITRARY_MESSAGE; - case SUBTYPE_MESSAGING_ALIAS_ASSIGNMENT: - return Messaging.ALIAS_ASSIGNMENT; - case SUBTYPE_MESSAGING_POLL_CREATION: - return Messaging.POLL_CREATION; - case SUBTYPE_MESSAGING_VOTE_CASTING: - return Messaging.VOTE_CASTING; - case SUBTYPE_MESSAGING_HUB_ANNOUNCEMENT: - return Messaging.HUB_ANNOUNCEMENT; - case SUBTYPE_MESSAGING_ACCOUNT_INFO: - return Messaging.ACCOUNT_INFO; - case SUBTYPE_MESSAGING_ALIAS_SELL: - return Messaging.ALIAS_SELL; - case SUBTYPE_MESSAGING_ALIAS_BUY: - return Messaging.ALIAS_BUY; - default: - return null; - } - case TYPE_COLORED_COINS: - switch (subtype) { - case SUBTYPE_COLORED_COINS_ASSET_ISSUANCE: - return ColoredCoins.ASSET_ISSUANCE; - case SUBTYPE_COLORED_COINS_ASSET_TRANSFER: - return ColoredCoins.ASSET_TRANSFER; - case SUBTYPE_COLORED_COINS_ASK_ORDER_PLACEMENT: - return ColoredCoins.ASK_ORDER_PLACEMENT; - case SUBTYPE_COLORED_COINS_BID_ORDER_PLACEMENT: - return ColoredCoins.BID_ORDER_PLACEMENT; - case SUBTYPE_COLORED_COINS_ASK_ORDER_CANCELLATION: - return ColoredCoins.ASK_ORDER_CANCELLATION; - case SUBTYPE_COLORED_COINS_BID_ORDER_CANCELLATION: - return ColoredCoins.BID_ORDER_CANCELLATION; - default: - return null; - } - case TYPE_DIGITAL_GOODS: - switch (subtype) { - case SUBTYPE_DIGITAL_GOODS_LISTING: - return DigitalGoods.LISTING; - case SUBTYPE_DIGITAL_GOODS_DELISTING: - return DigitalGoods.DELISTING; - case SUBTYPE_DIGITAL_GOODS_PRICE_CHANGE: - return DigitalGoods.PRICE_CHANGE; - case SUBTYPE_DIGITAL_GOODS_QUANTITY_CHANGE: - return DigitalGoods.QUANTITY_CHANGE; - case SUBTYPE_DIGITAL_GOODS_PURCHASE: - return DigitalGoods.PURCHASE; - case SUBTYPE_DIGITAL_GOODS_DELIVERY: - return DigitalGoods.DELIVERY; - case SUBTYPE_DIGITAL_GOODS_FEEDBACK: - return DigitalGoods.FEEDBACK; - case SUBTYPE_DIGITAL_GOODS_REFUND: - return DigitalGoods.REFUND; - default: - return null; - } - case TYPE_ACCOUNT_CONTROL: - switch (subtype) { - case SUBTYPE_ACCOUNT_CONTROL_EFFECTIVE_BALANCE_LEASING: - return AccountControl.EFFECTIVE_BALANCE_LEASING; - default: - return null; - } - case TYPE_BURST_MINING: - switch (subtype) { - case SUBTYPE_BURST_MINING_REWARD_RECIPIENT_ASSIGNMENT: - return BurstMining.REWARD_RECIPIENT_ASSIGNMENT; - default: - return null; - } - case TYPE_ADVANCED_PAYMENT: - switch (subtype) { - case SUBTYPE_ADVANCED_PAYMENT_ESCROW_CREATION: - return AdvancedPayment.ESCROW_CREATION; - case SUBTYPE_ADVANCED_PAYMENT_ESCROW_SIGN: - return AdvancedPayment.ESCROW_SIGN; - case SUBTYPE_ADVANCED_PAYMENT_ESCROW_RESULT: - return AdvancedPayment.ESCROW_RESULT; - case SUBTYPE_ADVANCED_PAYMENT_SUBSCRIPTION_SUBSCRIBE: - return AdvancedPayment.SUBSCRIPTION_SUBSCRIBE; - case SUBTYPE_ADVANCED_PAYMENT_SUBSCRIPTION_CANCEL: - return AdvancedPayment.SUBSCRIPTION_CANCEL; - case SUBTYPE_ADVANCED_PAYMENT_SUBSCRIPTION_PAYMENT: - return AdvancedPayment.SUBSCRIPTION_PAYMENT; - default: - return null; - } - case TYPE_AUTOMATED_TRANSACTIONS: - switch (subtype) { - case SUBTYPE_AT_CREATION: - return AutomatedTransactions.AUTOMATED_TRANSACTION_CREATION; - case SUBTYPE_AT_NXT_PAYMENT: - return AutomatedTransactions.AT_PAYMENT; - default: - return null; - } - default: - return null; - } - } - - private TransactionType() { - } - - public abstract byte getType(); - - public abstract byte getSubtype(); - - abstract Attachment.AbstractAttachment parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException; - - abstract Attachment.AbstractAttachment parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException; - - abstract void validateAttachment(Transaction transaction) throws NxtException.ValidationException; - - // return false iff double spending - final boolean applyUnconfirmed(Transaction transaction, Account senderAccount) { - long totalAmountNQT = Convert.safeAdd(transaction.getAmountNQT(), transaction.getFeeNQT()); - if (transaction.getReferencedTransactionFullHash() != null - && transaction.getTimestamp() > Constants.REFERENCED_TRANSACTION_FULL_HASH_BLOCK_TIMESTAMP) { - totalAmountNQT = Convert.safeAdd(totalAmountNQT, Constants.UNCONFIRMED_POOL_DEPOSIT_NQT); - } - if (senderAccount.getUnconfirmedBalanceNQT() < totalAmountNQT) { - return false; - } - senderAccount.addToUnconfirmedBalanceNQT(-totalAmountNQT); - if (!applyAttachmentUnconfirmed(transaction, senderAccount)) { - senderAccount.addToUnconfirmedBalanceNQT(totalAmountNQT); - return false; - } - return true; - } - - abstract boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount); - - final void apply(Transaction transaction, Account senderAccount, Account recipientAccount) { - senderAccount.addToBalanceNQT(- (Convert.safeAdd(transaction.getAmountNQT(), transaction.getFeeNQT()))); - if (transaction.getReferencedTransactionFullHash() != null - && transaction.getTimestamp() > Constants.REFERENCED_TRANSACTION_FULL_HASH_BLOCK_TIMESTAMP) { - senderAccount.addToUnconfirmedBalanceNQT(Constants.UNCONFIRMED_POOL_DEPOSIT_NQT); - } - if (recipientAccount != null) { - recipientAccount.addToBalanceAndUnconfirmedBalanceNQT(transaction.getAmountNQT()); - } - applyAttachment(transaction, senderAccount, recipientAccount); - } - - abstract void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount); - - final void undoUnconfirmed(Transaction transaction, Account senderAccount) { - undoAttachmentUnconfirmed(transaction, senderAccount); - senderAccount.addToUnconfirmedBalanceNQT(Convert.safeAdd(transaction.getAmountNQT(), transaction.getFeeNQT())); - if (transaction.getReferencedTransactionFullHash() != null - && transaction.getTimestamp() > Constants.REFERENCED_TRANSACTION_FULL_HASH_BLOCK_TIMESTAMP) { - senderAccount.addToUnconfirmedBalanceNQT(Constants.UNCONFIRMED_POOL_DEPOSIT_NQT); - } - } - - abstract void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount); - - boolean isDuplicate(Transaction transaction, Map> duplicates) { - return false; - } - - static boolean isDuplicate(TransactionType uniqueType, String key, Map> duplicates) { - Set typeDuplicates = duplicates.get(uniqueType); - if (typeDuplicates == null) { - typeDuplicates = new HashSet<>(); - duplicates.put(uniqueType, typeDuplicates); - } - return ! typeDuplicates.add(key); - } - - public abstract boolean hasRecipient(); - - public boolean isSigned() { - return true; - } - - @Override - public final String toString() { - return "type: " + getType() + ", subtype: " + getSubtype(); - } - - /* - Collection getPhasingTransactionTypes() { - return Collections.emptyList(); - } - - Collection getPhasedTransactionTypes() { - return Collections.emptyList(); - } - */ - - public static abstract class Payment extends TransactionType { - - private Payment() { - } - - @Override - public final byte getType() { - return TransactionType.TYPE_PAYMENT; - } - - @Override - final boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { - return true; - } - - @Override - final void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { - } - - @Override - final void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { - } - - @Override - final public boolean hasRecipient() { - return true; - } - - public static final TransactionType ORDINARY = new Payment() { - - @Override - public final byte getSubtype() { - return TransactionType.SUBTYPE_PAYMENT_ORDINARY_PAYMENT; - } - - @Override - Attachment.EmptyAttachment parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { - return Attachment.ORDINARY_PAYMENT; - } - - @Override - Attachment.EmptyAttachment parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { - return Attachment.ORDINARY_PAYMENT; - } - - @Override - void validateAttachment(Transaction transaction) throws NxtException.ValidationException { - if (transaction.getAmountNQT() <= 0 || transaction.getAmountNQT() >= Constants.MAX_BALANCE_NQT) { - throw new NxtException.NotValidException("Invalid ordinary payment"); - } - } - - }; - - } - - public static abstract class Messaging extends TransactionType { - - private Messaging() { - } - - @Override - public final byte getType() { - return TransactionType.TYPE_MESSAGING; - } - - @Override - final boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { - return true; - } - - @Override - final void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { - } - - public final static TransactionType ARBITRARY_MESSAGE = new Messaging() { - - @Override - public final byte getSubtype() { - return TransactionType.SUBTYPE_MESSAGING_ARBITRARY_MESSAGE; - } - - @Override - Attachment.EmptyAttachment parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { - return Attachment.ARBITRARY_MESSAGE; - } - - @Override - Attachment.EmptyAttachment parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { - return Attachment.ARBITRARY_MESSAGE; - } - - @Override - void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { - } - - @Override - void validateAttachment(Transaction transaction) throws NxtException.ValidationException { - Attachment attachment = transaction.getAttachment(); - if (transaction.getAmountNQT() != 0) { - throw new NxtException.NotValidException("Invalid arbitrary message: " + attachment.getJSONObject()); - } - if (Nxt.getBlockchain().getHeight() < Constants.DIGITAL_GOODS_STORE_BLOCK && transaction.getMessage() == null) { - throw new NxtException.NotCurrentlyValidException("Missing message appendix not allowed before DGS block"); - } - } - - @Override - public boolean hasRecipient() { - return true; - } - - }; - - public static final TransactionType ALIAS_ASSIGNMENT = new Messaging() { - - @Override - public final byte getSubtype() { - return TransactionType.SUBTYPE_MESSAGING_ALIAS_ASSIGNMENT; - } - - @Override - Attachment.MessagingAliasAssignment parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { - return new Attachment.MessagingAliasAssignment(buffer, transactionVersion); - } - - @Override - Attachment.MessagingAliasAssignment parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { - return new Attachment.MessagingAliasAssignment(attachmentData); - } - - @Override - void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { - Attachment.MessagingAliasAssignment attachment = (Attachment.MessagingAliasAssignment) transaction.getAttachment(); - Alias.addOrUpdateAlias(transaction, attachment); - } - - @Override - boolean isDuplicate(Transaction transaction, Map> duplicates) { - Attachment.MessagingAliasAssignment attachment = (Attachment.MessagingAliasAssignment) transaction.getAttachment(); - return isDuplicate(Messaging.ALIAS_ASSIGNMENT, attachment.getAliasName().toLowerCase(), duplicates); - } - - @Override - void validateAttachment(Transaction transaction) throws NxtException.ValidationException { - Attachment.MessagingAliasAssignment attachment = (Attachment.MessagingAliasAssignment) transaction.getAttachment(); - if (attachment.getAliasName().length() == 0 - || attachment.getAliasName().length() > Constants.MAX_ALIAS_LENGTH - || attachment.getAliasURI().length() > Constants.MAX_ALIAS_URI_LENGTH) { - throw new NxtException.NotValidException("Invalid alias assignment: " + attachment.getJSONObject()); - } - String normalizedAlias = attachment.getAliasName().toLowerCase(); - for (int i = 0; i < normalizedAlias.length(); i++) { - if (Constants.ALPHABET.indexOf(normalizedAlias.charAt(i)) < 0) { - throw new NxtException.NotValidException("Invalid alias name: " + normalizedAlias); - } - } - Alias alias = Alias.getAlias(normalizedAlias); - if (alias != null && alias.getAccountId() != transaction.getSenderId()) { - throw new NxtException.NotCurrentlyValidException("Alias already owned by another account: " + normalizedAlias); - } - } - - @Override - public boolean hasRecipient() { - return false; - } - - }; - - public static final TransactionType ALIAS_SELL = new Messaging() { - - @Override - public final byte getSubtype() { - return TransactionType.SUBTYPE_MESSAGING_ALIAS_SELL; - } - - @Override - Attachment.MessagingAliasSell parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { - return new Attachment.MessagingAliasSell(buffer, transactionVersion); - } - - @Override - Attachment.MessagingAliasSell parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { - return new Attachment.MessagingAliasSell(attachmentData); - } - - @Override - void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { - final Attachment.MessagingAliasSell attachment = - (Attachment.MessagingAliasSell) transaction.getAttachment(); - Alias.sellAlias(transaction, attachment); - } - - @Override - boolean isDuplicate(Transaction transaction, Map> duplicates) { - Attachment.MessagingAliasSell attachment = (Attachment.MessagingAliasSell) transaction.getAttachment(); - // not a bug, uniqueness is based on Messaging.ALIAS_ASSIGNMENT - return isDuplicate(Messaging.ALIAS_ASSIGNMENT, attachment.getAliasName().toLowerCase(), duplicates); - } - - @Override - void validateAttachment(Transaction transaction) throws NxtException.ValidationException { - if (Nxt.getBlockchain().getLastBlock().getHeight() < Constants.DIGITAL_GOODS_STORE_BLOCK) { - throw new NxtException.NotYetEnabledException("Alias transfer not yet enabled at height " + Nxt.getBlockchain().getLastBlock().getHeight()); - } - if (transaction.getAmountNQT() != 0) { - throw new NxtException.NotValidException("Invalid sell alias transaction: " + - transaction.getJSONObject()); - } - final Attachment.MessagingAliasSell attachment = - (Attachment.MessagingAliasSell) transaction.getAttachment(); - final String aliasName = attachment.getAliasName(); - if (aliasName == null || aliasName.length() == 0) { - throw new NxtException.NotValidException("Missing alias name"); - } - long priceNQT = attachment.getPriceNQT(); - if (priceNQT < 0 || priceNQT > Constants.MAX_BALANCE_NQT) { - throw new NxtException.NotValidException("Invalid alias sell price: " + priceNQT); - } - if (priceNQT == 0) { - if (Genesis.CREATOR_ID == transaction.getRecipientId()) { - throw new NxtException.NotValidException("Transferring aliases to Genesis account not allowed"); - } else if (transaction.getRecipientId() == 0) { - throw new NxtException.NotValidException("Missing alias transfer recipient"); - } - } - final Alias alias = Alias.getAlias(aliasName); - if (alias == null) { - throw new NxtException.NotCurrentlyValidException("Alias hasn't been registered yet: " + aliasName); - } else if (alias.getAccountId() != transaction.getSenderId()) { - throw new NxtException.NotCurrentlyValidException("Alias doesn't belong to sender: " + aliasName); - } - } - - @Override - public boolean hasRecipient() { - return true; - } - - }; - - public static final TransactionType ALIAS_BUY = new Messaging() { - - @Override - public final byte getSubtype() { - return TransactionType.SUBTYPE_MESSAGING_ALIAS_BUY; - } - - @Override - Attachment.MessagingAliasBuy parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { - return new Attachment.MessagingAliasBuy(buffer, transactionVersion); - } - - @Override - Attachment.MessagingAliasBuy parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { - return new Attachment.MessagingAliasBuy(attachmentData); - } - - @Override - void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { - final Attachment.MessagingAliasBuy attachment = - (Attachment.MessagingAliasBuy) transaction.getAttachment(); - final String aliasName = attachment.getAliasName(); - Alias.changeOwner(transaction.getSenderId(), aliasName, transaction.getBlockTimestamp()); - } - - @Override - boolean isDuplicate(Transaction transaction, Map> duplicates) { - Attachment.MessagingAliasBuy attachment = (Attachment.MessagingAliasBuy) transaction.getAttachment(); - // not a bug, uniqueness is based on Messaging.ALIAS_ASSIGNMENT - return isDuplicate(Messaging.ALIAS_ASSIGNMENT, attachment.getAliasName().toLowerCase(), duplicates); - } - - @Override - void validateAttachment(Transaction transaction) throws NxtException.ValidationException { - if (Nxt.getBlockchain().getLastBlock().getHeight() < Constants.DIGITAL_GOODS_STORE_BLOCK) { - throw new NxtException.NotYetEnabledException("Alias transfer not yet enabled at height " + Nxt.getBlockchain().getLastBlock().getHeight()); - } - final Attachment.MessagingAliasBuy attachment = - (Attachment.MessagingAliasBuy) transaction.getAttachment(); - final String aliasName = attachment.getAliasName(); - final Alias alias = Alias.getAlias(aliasName); - if (alias == null) { - throw new NxtException.NotCurrentlyValidException("Alias hasn't been registered yet: " + aliasName); - } else if (alias.getAccountId() != transaction.getRecipientId()) { - throw new NxtException.NotCurrentlyValidException("Alias is owned by account other than recipient: " - + Convert.toUnsignedLong(alias.getAccountId())); - } - Alias.Offer offer = Alias.getOffer(alias); - if (offer == null) { - throw new NxtException.NotCurrentlyValidException("Alias is not for sale: " + aliasName); - } - if (transaction.getAmountNQT() < offer.getPriceNQT()) { - String msg = "Price is too low for: " + aliasName + " (" - + transaction.getAmountNQT() + " < " + offer.getPriceNQT() + ")"; - throw new NxtException.NotCurrentlyValidException(msg); - } - if (offer.getBuyerId() != 0 && offer.getBuyerId() != transaction.getSenderId()) { - throw new NxtException.NotCurrentlyValidException("Wrong buyer for " + aliasName + ": " - + Convert.toUnsignedLong(transaction.getSenderId()) + " expected: " - + Convert.toUnsignedLong(offer.getBuyerId())); - } - } - - @Override - public boolean hasRecipient() { - return true; - } - - }; - - public final static TransactionType POLL_CREATION = new Messaging() { - @Override - public final byte getSubtype() { - return TransactionType.SUBTYPE_MESSAGING_POLL_CREATION; - } - - @Override - Attachment.MessagingPollCreation parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { - return new Attachment.MessagingPollCreation(buffer, transactionVersion); - } - - @Override - Attachment.MessagingPollCreation parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { - return new Attachment.MessagingPollCreation(attachmentData); - } - - @Override - void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { - Attachment.MessagingPollCreation attachment = (Attachment.MessagingPollCreation) transaction.getAttachment(); - Poll.addPoll(transaction, attachment); - } - - @Override - void validateAttachment(Transaction transaction) throws NxtException.ValidationException { - if (Nxt.getBlockchain().getLastBlock().getHeight() < Constants.VOTING_SYSTEM_BLOCK) { - throw new NxtException.NotYetEnabledException("Voting System not yet enabled at height " + Nxt.getBlockchain().getLastBlock().getHeight()); - } - Attachment.MessagingPollCreation attachment = (Attachment.MessagingPollCreation) transaction.getAttachment(); - for (int i = 0; i < attachment.getPollOptions().length; i++) { - if (attachment.getPollOptions()[i].length() > Constants.MAX_POLL_OPTION_LENGTH) { - throw new NxtException.NotValidException("Invalid poll options length: " + attachment.getJSONObject()); - } - } - if (attachment.getPollName().length() > Constants.MAX_POLL_NAME_LENGTH - || attachment.getPollDescription().length() > Constants.MAX_POLL_DESCRIPTION_LENGTH - || attachment.getPollOptions().length > Constants.MAX_POLL_OPTION_COUNT) { - throw new NxtException.NotValidException("Invalid poll attachment: " + attachment.getJSONObject()); - } - } - - @Override - public boolean hasRecipient() { - return false; - } - - }; - - public final static TransactionType VOTE_CASTING = new Messaging() { - - @Override - public final byte getSubtype() { - return TransactionType.SUBTYPE_MESSAGING_VOTE_CASTING; - } - - @Override - Attachment.MessagingVoteCasting parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { - return new Attachment.MessagingVoteCasting(buffer, transactionVersion); - } - - @Override - Attachment.MessagingVoteCasting parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { - return new Attachment.MessagingVoteCasting(attachmentData); - } - - @Override - void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { - Attachment.MessagingVoteCasting attachment = (Attachment.MessagingVoteCasting) transaction.getAttachment(); - Poll poll = Poll.getPoll(attachment.getPollId()); - if (poll != null) { - Vote.addVote(transaction, attachment); - } - } - - @Override - void validateAttachment(Transaction transaction) throws NxtException.ValidationException { - if (Nxt.getBlockchain().getLastBlock().getHeight() < Constants.VOTING_SYSTEM_BLOCK) { - throw new NxtException.NotYetEnabledException("Voting System not yet enabled at height " + Nxt.getBlockchain().getLastBlock().getHeight()); - } - Attachment.MessagingVoteCasting attachment = (Attachment.MessagingVoteCasting) transaction.getAttachment(); - if (attachment.getPollId() == 0 || attachment.getPollVote() == null - || attachment.getPollVote().length > Constants.MAX_POLL_OPTION_COUNT) { - throw new NxtException.NotValidException("Invalid vote casting attachment: " + attachment.getJSONObject()); - } - if (Poll.getPoll(attachment.getPollId()) == null) { - throw new NxtException.NotCurrentlyValidException("Invalid poll: " + Convert.toUnsignedLong(attachment.getPollId())); - } - } - - @Override - public boolean hasRecipient() { - return false; - } - - }; - - public static final TransactionType HUB_ANNOUNCEMENT = new Messaging() { - - @Override - public final byte getSubtype() { - return TransactionType.SUBTYPE_MESSAGING_HUB_ANNOUNCEMENT; - } - - @Override - Attachment.MessagingHubAnnouncement parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { - return new Attachment.MessagingHubAnnouncement(buffer, transactionVersion); - } - - @Override - Attachment.MessagingHubAnnouncement parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { - return new Attachment.MessagingHubAnnouncement(attachmentData); - } - - @Override - void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { - Attachment.MessagingHubAnnouncement attachment = (Attachment.MessagingHubAnnouncement) transaction.getAttachment(); - Hub.addOrUpdateHub(transaction, attachment); - } - - @Override - void validateAttachment(Transaction transaction) throws NxtException.ValidationException { - if (Nxt.getBlockchain().getLastBlock().getHeight() < Constants.TRANSPARENT_FORGING_BLOCK_7) { - throw new NxtException.NotYetEnabledException("Hub terminal announcement not yet enabled at height " + Nxt.getBlockchain().getLastBlock().getHeight()); - } - Attachment.MessagingHubAnnouncement attachment = (Attachment.MessagingHubAnnouncement) transaction.getAttachment(); - if (attachment.getMinFeePerByteNQT() < 0 || attachment.getMinFeePerByteNQT() > Constants.MAX_BALANCE_NQT - || attachment.getUris().length > Constants.MAX_HUB_ANNOUNCEMENT_URIS) { - // cfb: "0" is allowed to show that another way to determine the min fee should be used - throw new NxtException.NotValidException("Invalid hub terminal announcement: " + attachment.getJSONObject()); - } - for (String uri : attachment.getUris()) { - if (uri.length() > Constants.MAX_HUB_ANNOUNCEMENT_URI_LENGTH) { - throw new NxtException.NotValidException("Invalid URI length: " + uri.length()); - } - //TODO: also check URI validity here? - } - } - - @Override - public boolean hasRecipient() { - return false; - } - - }; - - public static final Messaging ACCOUNT_INFO = new Messaging() { - - @Override - public byte getSubtype() { - return TransactionType.SUBTYPE_MESSAGING_ACCOUNT_INFO; - } - - @Override - Attachment.MessagingAccountInfo parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { - return new Attachment.MessagingAccountInfo(buffer, transactionVersion); - } - - @Override - Attachment.MessagingAccountInfo parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { - return new Attachment.MessagingAccountInfo(attachmentData); - } - - @Override - void validateAttachment(Transaction transaction) throws NxtException.ValidationException { - Attachment.MessagingAccountInfo attachment = (Attachment.MessagingAccountInfo)transaction.getAttachment(); - if (attachment.getName().length() > Constants.MAX_ACCOUNT_NAME_LENGTH - || attachment.getDescription().length() > Constants.MAX_ACCOUNT_DESCRIPTION_LENGTH - ) { - throw new NxtException.NotValidException("Invalid account info issuance: " + attachment.getJSONObject()); - } - } - - @Override - void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { - Attachment.MessagingAccountInfo attachment = (Attachment.MessagingAccountInfo) transaction.getAttachment(); - senderAccount.setAccountInfo(attachment.getName(), attachment.getDescription()); - } - - @Override - public boolean hasRecipient() { - return false; - } - - }; - - } - - public static abstract class ColoredCoins extends TransactionType { - - private ColoredCoins() {} - - @Override - public final byte getType() { - return TransactionType.TYPE_COLORED_COINS; - } - - public static final TransactionType ASSET_ISSUANCE = new ColoredCoins() { - - @Override - public final byte getSubtype() { - return TransactionType.SUBTYPE_COLORED_COINS_ASSET_ISSUANCE; - } - - @Override - public Fee getBaselineFee() { - return BASELINE_ASSET_ISSUANCE_FEE; - } - - @Override - public Fee getNextFee() { - return NEXT_ASSET_ISSUANCE_FEE; - } - - @Override - Attachment.ColoredCoinsAssetIssuance parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { - return new Attachment.ColoredCoinsAssetIssuance(buffer, transactionVersion); - } - - @Override - Attachment.ColoredCoinsAssetIssuance parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { - return new Attachment.ColoredCoinsAssetIssuance(attachmentData); - } - - @Override - boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { - return true; - } - - @Override - void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { - Attachment.ColoredCoinsAssetIssuance attachment = (Attachment.ColoredCoinsAssetIssuance) transaction.getAttachment(); - long assetId = transaction.getId(); - Asset.addAsset(transaction, attachment); - senderAccount.addToAssetAndUnconfirmedAssetBalanceQNT(assetId, attachment.getQuantityQNT()); - } - - @Override - void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { - } - - @Override - void validateAttachment(Transaction transaction) throws NxtException.ValidationException { - Attachment.ColoredCoinsAssetIssuance attachment = (Attachment.ColoredCoinsAssetIssuance)transaction.getAttachment(); - if (attachment.getName().length() < Constants.MIN_ASSET_NAME_LENGTH - || attachment.getName().length() > Constants.MAX_ASSET_NAME_LENGTH - || attachment.getDescription().length() > Constants.MAX_ASSET_DESCRIPTION_LENGTH - || attachment.getDecimals() < 0 || attachment.getDecimals() > 8 - || attachment.getQuantityQNT() <= 0 - || attachment.getQuantityQNT() > Constants.MAX_ASSET_QUANTITY_QNT - ) { - throw new NxtException.NotValidException("Invalid asset issuance: " + attachment.getJSONObject()); - } - String normalizedName = attachment.getName().toLowerCase(); - for (int i = 0; i < normalizedName.length(); i++) { - if (Constants.ALPHABET.indexOf(normalizedName.charAt(i)) < 0) { - throw new NxtException.NotValidException("Invalid asset name: " + normalizedName); - } - } - } - - @Override - public boolean hasRecipient() { - return false; - } - - }; - - public static final TransactionType ASSET_TRANSFER = new ColoredCoins() { - - @Override - public final byte getSubtype() { - return TransactionType.SUBTYPE_COLORED_COINS_ASSET_TRANSFER; - } - - @Override - Attachment.ColoredCoinsAssetTransfer parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { - return new Attachment.ColoredCoinsAssetTransfer(buffer, transactionVersion); - } - - @Override - Attachment.ColoredCoinsAssetTransfer parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { - return new Attachment.ColoredCoinsAssetTransfer(attachmentData); - } - - @Override - boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { - Attachment.ColoredCoinsAssetTransfer attachment = (Attachment.ColoredCoinsAssetTransfer) transaction.getAttachment(); - long unconfirmedAssetBalance = senderAccount.getUnconfirmedAssetBalanceQNT(attachment.getAssetId()); - if (unconfirmedAssetBalance >= 0 && unconfirmedAssetBalance >= attachment.getQuantityQNT()) { - senderAccount.addToUnconfirmedAssetBalanceQNT(attachment.getAssetId(), -attachment.getQuantityQNT()); - return true; - } - return false; - } - - @Override - void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { - Attachment.ColoredCoinsAssetTransfer attachment = (Attachment.ColoredCoinsAssetTransfer) transaction.getAttachment(); - senderAccount.addToAssetBalanceQNT(attachment.getAssetId(), -attachment.getQuantityQNT()); - recipientAccount.addToAssetAndUnconfirmedAssetBalanceQNT(attachment.getAssetId(), attachment.getQuantityQNT()); - AssetTransfer.addAssetTransfer(transaction, attachment); - } - - @Override - void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { - Attachment.ColoredCoinsAssetTransfer attachment = (Attachment.ColoredCoinsAssetTransfer) transaction.getAttachment(); - senderAccount.addToUnconfirmedAssetBalanceQNT(attachment.getAssetId(), attachment.getQuantityQNT()); - } - - @Override - void validateAttachment(Transaction transaction) throws NxtException.ValidationException { - Attachment.ColoredCoinsAssetTransfer attachment = (Attachment.ColoredCoinsAssetTransfer)transaction.getAttachment(); - if (transaction.getAmountNQT() != 0 - || attachment.getComment() != null && attachment.getComment().length() > Constants.MAX_ASSET_TRANSFER_COMMENT_LENGTH - || attachment.getAssetId() == 0) { - throw new NxtException.NotValidException("Invalid asset transfer amount or comment: " + attachment.getJSONObject()); - } - if (transaction.getVersion() > 0 && attachment.getComment() != null) { - throw new NxtException.NotValidException("Asset transfer comments no longer allowed, use message " + - "or encrypted message appendix instead"); - } - Asset asset = Asset.getAsset(attachment.getAssetId()); - if (attachment.getQuantityQNT() <= 0 || (asset != null && attachment.getQuantityQNT() > asset.getQuantityQNT())) { - throw new NxtException.NotValidException("Invalid asset transfer asset or quantity: " + attachment.getJSONObject()); - } - if (asset == null) { - throw new NxtException.NotCurrentlyValidException("Asset " + Convert.toUnsignedLong(attachment.getAssetId()) + - " does not exist yet"); - } - } - - @Override - public boolean hasRecipient() { - return true; - } - - }; - - abstract static class ColoredCoinsOrderPlacement extends ColoredCoins { - - @Override - final void validateAttachment(Transaction transaction) throws NxtException.ValidationException { - Attachment.ColoredCoinsOrderPlacement attachment = (Attachment.ColoredCoinsOrderPlacement)transaction.getAttachment(); - if (attachment.getPriceNQT() <= 0 || attachment.getPriceNQT() > Constants.MAX_BALANCE_NQT - || attachment.getAssetId() == 0) { - throw new NxtException.NotValidException("Invalid asset order placement: " + attachment.getJSONObject()); - } - Asset asset = Asset.getAsset(attachment.getAssetId()); - if (attachment.getQuantityQNT() <= 0 || (asset != null && attachment.getQuantityQNT() > asset.getQuantityQNT())) { - throw new NxtException.NotValidException("Invalid asset order placement asset or quantity: " + attachment.getJSONObject()); - } - if (asset == null) { - throw new NxtException.NotCurrentlyValidException("Asset " + Convert.toUnsignedLong(attachment.getAssetId()) + - " does not exist yet"); - } - } - - @Override - final public boolean hasRecipient() { - return false; - } - - } - - public static final TransactionType ASK_ORDER_PLACEMENT = new ColoredCoins.ColoredCoinsOrderPlacement() { - - @Override - public final byte getSubtype() { - return TransactionType.SUBTYPE_COLORED_COINS_ASK_ORDER_PLACEMENT; - } - - @Override - Attachment.ColoredCoinsAskOrderPlacement parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { - return new Attachment.ColoredCoinsAskOrderPlacement(buffer, transactionVersion); - } - - @Override - Attachment.ColoredCoinsAskOrderPlacement parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { - return new Attachment.ColoredCoinsAskOrderPlacement(attachmentData); - } - - @Override - boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { - Attachment.ColoredCoinsAskOrderPlacement attachment = (Attachment.ColoredCoinsAskOrderPlacement) transaction.getAttachment(); - long unconfirmedAssetBalance = senderAccount.getUnconfirmedAssetBalanceQNT(attachment.getAssetId()); - if (unconfirmedAssetBalance >= 0 && unconfirmedAssetBalance >= attachment.getQuantityQNT()) { - senderAccount.addToUnconfirmedAssetBalanceQNT(attachment.getAssetId(), -attachment.getQuantityQNT()); - return true; - } - return false; - } - - @Override - void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { - Attachment.ColoredCoinsAskOrderPlacement attachment = (Attachment.ColoredCoinsAskOrderPlacement) transaction.getAttachment(); - if (Asset.getAsset(attachment.getAssetId()) != null) { - Order.Ask.addOrder(transaction, attachment); - } - } - - @Override - void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { - Attachment.ColoredCoinsAskOrderPlacement attachment = (Attachment.ColoredCoinsAskOrderPlacement) transaction.getAttachment(); - senderAccount.addToUnconfirmedAssetBalanceQNT(attachment.getAssetId(), attachment.getQuantityQNT()); - } - - }; - - public final static TransactionType BID_ORDER_PLACEMENT = new ColoredCoins.ColoredCoinsOrderPlacement() { - - @Override - public final byte getSubtype() { - return TransactionType.SUBTYPE_COLORED_COINS_BID_ORDER_PLACEMENT; - } - - @Override - Attachment.ColoredCoinsBidOrderPlacement parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { - return new Attachment.ColoredCoinsBidOrderPlacement(buffer, transactionVersion); - } - - @Override - Attachment.ColoredCoinsBidOrderPlacement parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { - return new Attachment.ColoredCoinsBidOrderPlacement(attachmentData); - } - - @Override - boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { - Attachment.ColoredCoinsBidOrderPlacement attachment = (Attachment.ColoredCoinsBidOrderPlacement) transaction.getAttachment(); - if (senderAccount.getUnconfirmedBalanceNQT() >= Convert.safeMultiply(attachment.getQuantityQNT(), attachment.getPriceNQT())) { - senderAccount.addToUnconfirmedBalanceNQT(-Convert.safeMultiply(attachment.getQuantityQNT(), attachment.getPriceNQT())); - return true; - } - return false; - } - - @Override - void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { - Attachment.ColoredCoinsBidOrderPlacement attachment = (Attachment.ColoredCoinsBidOrderPlacement) transaction.getAttachment(); - if (Asset.getAsset(attachment.getAssetId()) != null) { - Order.Bid.addOrder(transaction, attachment); - } - } - - @Override - void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { - Attachment.ColoredCoinsBidOrderPlacement attachment = (Attachment.ColoredCoinsBidOrderPlacement) transaction.getAttachment(); - senderAccount.addToUnconfirmedBalanceNQT(Convert.safeMultiply(attachment.getQuantityQNT(), attachment.getPriceNQT())); - } - - }; - - abstract static class ColoredCoinsOrderCancellation extends ColoredCoins { - - @Override - final boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { - return true; - } - - @Override - final void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { - } - - @Override - public boolean hasRecipient() { - return false; - } - - } - - public static final TransactionType ASK_ORDER_CANCELLATION = new ColoredCoins.ColoredCoinsOrderCancellation() { - - @Override - public final byte getSubtype() { - return TransactionType.SUBTYPE_COLORED_COINS_ASK_ORDER_CANCELLATION; - } - - @Override - Attachment.ColoredCoinsAskOrderCancellation parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { - return new Attachment.ColoredCoinsAskOrderCancellation(buffer, transactionVersion); - } - - @Override - Attachment.ColoredCoinsAskOrderCancellation parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { - return new Attachment.ColoredCoinsAskOrderCancellation(attachmentData); - } - - @Override - void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { - Attachment.ColoredCoinsAskOrderCancellation attachment = (Attachment.ColoredCoinsAskOrderCancellation) transaction.getAttachment(); - Order order = Order.Ask.getAskOrder(attachment.getOrderId()); - Order.Ask.removeOrder(attachment.getOrderId()); - if (order != null) { - senderAccount.addToUnconfirmedAssetBalanceQNT(order.getAssetId(), order.getQuantityQNT()); - } - } - - @Override - void validateAttachment(Transaction transaction) throws NxtException.ValidationException { - Attachment.ColoredCoinsAskOrderCancellation attachment = (Attachment.ColoredCoinsAskOrderCancellation) transaction.getAttachment(); - Order ask = Order.Ask.getAskOrder(attachment.getOrderId()); - if(ask == null) { - throw new NxtException.NotCurrentlyValidException("Invalid ask order: " + Convert.toUnsignedLong(attachment.getOrderId())); - } - if(ask.getAccountId() != transaction.getSenderId()) { - throw new NxtException.NotValidException("Order " + Convert.toUnsignedLong(attachment.getOrderId()) + " was created by account " - + Convert.toUnsignedLong(ask.getAccountId())); - } - } - - }; - - public static final TransactionType BID_ORDER_CANCELLATION = new ColoredCoins.ColoredCoinsOrderCancellation() { - - @Override - public final byte getSubtype() { - return TransactionType.SUBTYPE_COLORED_COINS_BID_ORDER_CANCELLATION; - } - - @Override - Attachment.ColoredCoinsBidOrderCancellation parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { - return new Attachment.ColoredCoinsBidOrderCancellation(buffer, transactionVersion); - } - - @Override - Attachment.ColoredCoinsBidOrderCancellation parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { - return new Attachment.ColoredCoinsBidOrderCancellation(attachmentData); - } - - @Override - void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { - Attachment.ColoredCoinsBidOrderCancellation attachment = (Attachment.ColoredCoinsBidOrderCancellation) transaction.getAttachment(); - Order order = Order.Bid.getBidOrder(attachment.getOrderId()); - Order.Bid.removeOrder(attachment.getOrderId()); - if (order != null) { - senderAccount.addToUnconfirmedBalanceNQT(Convert.safeMultiply(order.getQuantityQNT(), order.getPriceNQT())); - } - } - - @Override - void validateAttachment(Transaction transaction) throws NxtException.ValidationException { - Attachment.ColoredCoinsBidOrderCancellation attachment = (Attachment.ColoredCoinsBidOrderCancellation) transaction.getAttachment(); - Order bid = Order.Bid.getBidOrder(attachment.getOrderId()); - if(bid == null) { - throw new NxtException.NotCurrentlyValidException("Invalid bid order: " + Convert.toUnsignedLong(attachment.getOrderId())); - } - if(bid.getAccountId() != transaction.getSenderId()) { - throw new NxtException.NotValidException("Order " + Convert.toUnsignedLong(attachment.getOrderId()) + " was created by account " - + Convert.toUnsignedLong(bid.getAccountId())); - } - } - - }; - } - - public static abstract class DigitalGoods extends TransactionType { - - private DigitalGoods() { - } - - @Override - public final byte getType() { - return TransactionType.TYPE_DIGITAL_GOODS; - } - - @Override - boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { - return true; - } - - @Override - void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { - } - - @Override - final void validateAttachment(Transaction transaction) throws NxtException.ValidationException { - if (Nxt.getBlockchain().getLastBlock().getHeight() < Constants.DIGITAL_GOODS_STORE_BLOCK) { - throw new NxtException.NotYetEnabledException("Digital goods listing not yet enabled at height " + Nxt.getBlockchain().getLastBlock().getHeight()); - } - if (transaction.getAmountNQT() != 0) { - throw new NxtException.NotValidException("Invalid digital goods transaction"); - } - doValidateAttachment(transaction); - } - - abstract void doValidateAttachment(Transaction transaction) throws NxtException.ValidationException; - - - public static final TransactionType LISTING = new DigitalGoods() { - - @Override - public final byte getSubtype() { - return TransactionType.SUBTYPE_DIGITAL_GOODS_LISTING; - } - - @Override - Attachment.DigitalGoodsListing parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { - return new Attachment.DigitalGoodsListing(buffer, transactionVersion); - } - - @Override - Attachment.DigitalGoodsListing parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { - return new Attachment.DigitalGoodsListing(attachmentData); - } - - @Override - void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { - Attachment.DigitalGoodsListing attachment = (Attachment.DigitalGoodsListing) transaction.getAttachment(); - DigitalGoodsStore.listGoods(transaction, attachment); - } - - @Override - void doValidateAttachment(Transaction transaction) throws NxtException.ValidationException { - Attachment.DigitalGoodsListing attachment = (Attachment.DigitalGoodsListing) transaction.getAttachment(); - if (attachment.getName().length() == 0 - || attachment.getName().length() > Constants.MAX_DGS_LISTING_NAME_LENGTH - || attachment.getDescription().length() > Constants.MAX_DGS_LISTING_DESCRIPTION_LENGTH - || attachment.getTags().length() > Constants.MAX_DGS_LISTING_TAGS_LENGTH - || attachment.getQuantity() < 0 || attachment.getQuantity() > Constants.MAX_DGS_LISTING_QUANTITY - || attachment.getPriceNQT() <= 0 || attachment.getPriceNQT() > Constants.MAX_BALANCE_NQT) { - throw new NxtException.NotValidException("Invalid digital goods listing: " + attachment.getJSONObject()); - } - } - - @Override - public boolean hasRecipient() { - return false; - } - - }; - - public static final TransactionType DELISTING = new DigitalGoods() { - - @Override - public final byte getSubtype() { - return TransactionType.SUBTYPE_DIGITAL_GOODS_DELISTING; - } - - @Override - Attachment.DigitalGoodsDelisting parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { - return new Attachment.DigitalGoodsDelisting(buffer, transactionVersion); - } - - @Override - Attachment.DigitalGoodsDelisting parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { - return new Attachment.DigitalGoodsDelisting(attachmentData); - } - - @Override - void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { - Attachment.DigitalGoodsDelisting attachment = (Attachment.DigitalGoodsDelisting) transaction.getAttachment(); - DigitalGoodsStore.delistGoods(attachment.getGoodsId()); - } - - @Override - void doValidateAttachment(Transaction transaction) throws NxtException.ValidationException { - Attachment.DigitalGoodsDelisting attachment = (Attachment.DigitalGoodsDelisting) transaction.getAttachment(); - DigitalGoodsStore.Goods goods = DigitalGoodsStore.getGoods(attachment.getGoodsId()); - if (goods != null && transaction.getSenderId() != goods.getSellerId()) { - throw new NxtException.NotValidException("Invalid digital goods delisting - seller is different: " + attachment.getJSONObject()); - } - if (goods == null || goods.isDelisted()) { - throw new NxtException.NotCurrentlyValidException("Goods " + Convert.toUnsignedLong(attachment.getGoodsId()) + - "not yet listed or already delisted"); - } - } - - @Override - boolean isDuplicate(Transaction transaction, Map> duplicates) { - Attachment.DigitalGoodsDelisting attachment = (Attachment.DigitalGoodsDelisting) transaction.getAttachment(); - return isDuplicate(DigitalGoods.DELISTING, Convert.toUnsignedLong(attachment.getGoodsId()), duplicates); - } - - @Override - public boolean hasRecipient() { - return false; - } - - }; - - public static final TransactionType PRICE_CHANGE = new DigitalGoods() { - - @Override - public final byte getSubtype() { - return TransactionType.SUBTYPE_DIGITAL_GOODS_PRICE_CHANGE; - } - - @Override - Attachment.DigitalGoodsPriceChange parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { - return new Attachment.DigitalGoodsPriceChange(buffer, transactionVersion); - } - - @Override - Attachment.DigitalGoodsPriceChange parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { - return new Attachment.DigitalGoodsPriceChange(attachmentData); - } - - @Override - void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { - Attachment.DigitalGoodsPriceChange attachment = (Attachment.DigitalGoodsPriceChange) transaction.getAttachment(); - DigitalGoodsStore.changePrice(attachment.getGoodsId(), attachment.getPriceNQT()); - } - - @Override - void doValidateAttachment(Transaction transaction) throws NxtException.ValidationException { - Attachment.DigitalGoodsPriceChange attachment = (Attachment.DigitalGoodsPriceChange) transaction.getAttachment(); - DigitalGoodsStore.Goods goods = DigitalGoodsStore.getGoods(attachment.getGoodsId()); - if (attachment.getPriceNQT() <= 0 || attachment.getPriceNQT() > Constants.MAX_BALANCE_NQT - || (goods != null && transaction.getSenderId() != goods.getSellerId())) { - throw new NxtException.NotValidException("Invalid digital goods price change: " + attachment.getJSONObject()); - } - if (goods == null || goods.isDelisted()) { - throw new NxtException.NotCurrentlyValidException("Goods " + Convert.toUnsignedLong(attachment.getGoodsId()) + - "not yet listed or already delisted"); - } - } - - @Override - boolean isDuplicate(Transaction transaction, Map> duplicates) { - Attachment.DigitalGoodsPriceChange attachment = (Attachment.DigitalGoodsPriceChange) transaction.getAttachment(); - // not a bug, uniqueness is based on DigitalGoods.DELISTING - return isDuplicate(DigitalGoods.DELISTING, Convert.toUnsignedLong(attachment.getGoodsId()), duplicates); - } - - @Override - public boolean hasRecipient() { - return false; - } - - }; - - public static final TransactionType QUANTITY_CHANGE = new DigitalGoods() { - - @Override - public final byte getSubtype() { - return TransactionType.SUBTYPE_DIGITAL_GOODS_QUANTITY_CHANGE; - } - - @Override - Attachment.DigitalGoodsQuantityChange parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { - return new Attachment.DigitalGoodsQuantityChange(buffer, transactionVersion); - } - - @Override - Attachment.DigitalGoodsQuantityChange parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { - return new Attachment.DigitalGoodsQuantityChange(attachmentData); - } - - @Override - void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { - Attachment.DigitalGoodsQuantityChange attachment = (Attachment.DigitalGoodsQuantityChange) transaction.getAttachment(); - DigitalGoodsStore.changeQuantity(attachment.getGoodsId(), attachment.getDeltaQuantity()); - } - - @Override - void doValidateAttachment(Transaction transaction) throws NxtException.ValidationException { - Attachment.DigitalGoodsQuantityChange attachment = (Attachment.DigitalGoodsQuantityChange) transaction.getAttachment(); - DigitalGoodsStore.Goods goods = DigitalGoodsStore.getGoods(attachment.getGoodsId()); - if (attachment.getDeltaQuantity() < -Constants.MAX_DGS_LISTING_QUANTITY - || attachment.getDeltaQuantity() > Constants.MAX_DGS_LISTING_QUANTITY - || (goods != null && transaction.getSenderId() != goods.getSellerId())) { - throw new NxtException.NotValidException("Invalid digital goods quantity change: " + attachment.getJSONObject()); - } - if (goods == null || goods.isDelisted()) { - throw new NxtException.NotCurrentlyValidException("Goods " + Convert.toUnsignedLong(attachment.getGoodsId()) + - "not yet listed or already delisted"); - } - } - - @Override - boolean isDuplicate(Transaction transaction, Map> duplicates) { - Attachment.DigitalGoodsQuantityChange attachment = (Attachment.DigitalGoodsQuantityChange) transaction.getAttachment(); - // not a bug, uniqueness is based on DigitalGoods.DELISTING - return isDuplicate(DigitalGoods.DELISTING, Convert.toUnsignedLong(attachment.getGoodsId()), duplicates); - } - - @Override - public boolean hasRecipient() { - return false; - } - - }; - - public static final TransactionType PURCHASE = new DigitalGoods() { - - @Override - public final byte getSubtype() { - return TransactionType.SUBTYPE_DIGITAL_GOODS_PURCHASE; - } - - @Override - Attachment.DigitalGoodsPurchase parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { - return new Attachment.DigitalGoodsPurchase(buffer, transactionVersion); - } - - @Override - Attachment.DigitalGoodsPurchase parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { - return new Attachment.DigitalGoodsPurchase(attachmentData); - } - - @Override - boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { - Attachment.DigitalGoodsPurchase attachment = (Attachment.DigitalGoodsPurchase) transaction.getAttachment(); - if (senderAccount.getUnconfirmedBalanceNQT() >= Convert.safeMultiply(attachment.getQuantity(), attachment.getPriceNQT())) { - senderAccount.addToUnconfirmedBalanceNQT(-Convert.safeMultiply(attachment.getQuantity(), attachment.getPriceNQT())); - return true; - } - return false; - } - - @Override - void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { - Attachment.DigitalGoodsPurchase attachment = (Attachment.DigitalGoodsPurchase) transaction.getAttachment(); - senderAccount.addToUnconfirmedBalanceNQT(Convert.safeMultiply(attachment.getQuantity(), attachment.getPriceNQT())); - } - - @Override - void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { - Attachment.DigitalGoodsPurchase attachment = (Attachment.DigitalGoodsPurchase) transaction.getAttachment(); - DigitalGoodsStore.purchase(transaction, attachment); - } - - @Override - void doValidateAttachment(Transaction transaction) throws NxtException.ValidationException { - Attachment.DigitalGoodsPurchase attachment = (Attachment.DigitalGoodsPurchase) transaction.getAttachment(); - DigitalGoodsStore.Goods goods = DigitalGoodsStore.getGoods(attachment.getGoodsId()); - if (attachment.getQuantity() <= 0 || attachment.getQuantity() > Constants.MAX_DGS_LISTING_QUANTITY - || attachment.getPriceNQT() <= 0 || attachment.getPriceNQT() > Constants.MAX_BALANCE_NQT - || (goods != null && goods.getSellerId() != transaction.getRecipientId())) { - throw new NxtException.NotValidException("Invalid digital goods purchase: " + attachment.getJSONObject()); - } - if (transaction.getEncryptedMessage() != null && ! transaction.getEncryptedMessage().isText()) { - throw new NxtException.NotValidException("Only text encrypted messages allowed"); - } - if (goods == null || goods.isDelisted()) { - throw new NxtException.NotCurrentlyValidException("Goods " + Convert.toUnsignedLong(attachment.getGoodsId()) + - "not yet listed or already delisted"); - } - if (attachment.getQuantity() > goods.getQuantity() || attachment.getPriceNQT() != goods.getPriceNQT()) { - throw new NxtException.NotCurrentlyValidException("Goods price or quantity changed: " + attachment.getJSONObject()); - } - if (attachment.getDeliveryDeadlineTimestamp() <= Nxt.getBlockchain().getLastBlock().getTimestamp()) { - throw new NxtException.NotCurrentlyValidException("Delivery deadline has already expired: " + attachment.getDeliveryDeadlineTimestamp()); - } - } - - @Override - public boolean hasRecipient() { - return true; - } - - }; - - public static final TransactionType DELIVERY = new DigitalGoods() { - - @Override - public final byte getSubtype() { - return TransactionType.SUBTYPE_DIGITAL_GOODS_DELIVERY; - } - - @Override - Attachment.DigitalGoodsDelivery parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { - return new Attachment.DigitalGoodsDelivery(buffer, transactionVersion); - } - - @Override - Attachment.DigitalGoodsDelivery parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { - return new Attachment.DigitalGoodsDelivery(attachmentData); - } - - @Override - void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { - Attachment.DigitalGoodsDelivery attachment = (Attachment.DigitalGoodsDelivery)transaction.getAttachment(); - DigitalGoodsStore.deliver(transaction, attachment); - } - - @Override - void doValidateAttachment(Transaction transaction) throws NxtException.ValidationException { - Attachment.DigitalGoodsDelivery attachment = (Attachment.DigitalGoodsDelivery) transaction.getAttachment(); - DigitalGoodsStore.Purchase purchase = DigitalGoodsStore.getPendingPurchase(attachment.getPurchaseId()); - if (attachment.getGoods().getData().length > Constants.MAX_DGS_GOODS_LENGTH - || attachment.getGoods().getData().length == 0 - || attachment.getGoods().getNonce().length != 32 - || attachment.getDiscountNQT() < 0 || attachment.getDiscountNQT() > Constants.MAX_BALANCE_NQT - || (purchase != null && - (purchase.getBuyerId() != transaction.getRecipientId() - || transaction.getSenderId() != purchase.getSellerId() - || attachment.getDiscountNQT() > Convert.safeMultiply(purchase.getPriceNQT(), purchase.getQuantity())))) { - throw new NxtException.NotValidException("Invalid digital goods delivery: " + attachment.getJSONObject()); - } - if (purchase == null || purchase.getEncryptedGoods() != null) { - throw new NxtException.NotCurrentlyValidException("Purchase does not exist yet, or already delivered: " - + attachment.getJSONObject()); - } - } - - @Override - boolean isDuplicate(Transaction transaction, Map> duplicates) { - Attachment.DigitalGoodsDelivery attachment = (Attachment.DigitalGoodsDelivery) transaction.getAttachment(); - return isDuplicate(DigitalGoods.DELIVERY, Convert.toUnsignedLong(attachment.getPurchaseId()), duplicates); - } - - @Override - public boolean hasRecipient() { - return true; - } - - }; - - public static final TransactionType FEEDBACK = new DigitalGoods() { - - @Override - public final byte getSubtype() { - return TransactionType.SUBTYPE_DIGITAL_GOODS_FEEDBACK; - } - - @Override - Attachment.DigitalGoodsFeedback parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { - return new Attachment.DigitalGoodsFeedback(buffer, transactionVersion); - } - - @Override - Attachment.DigitalGoodsFeedback parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { - return new Attachment.DigitalGoodsFeedback(attachmentData); - } - - @Override - void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { - Attachment.DigitalGoodsFeedback attachment = (Attachment.DigitalGoodsFeedback)transaction.getAttachment(); - DigitalGoodsStore.feedback(attachment.getPurchaseId(), transaction.getEncryptedMessage(), transaction.getMessage()); - } - - @Override - void doValidateAttachment(Transaction transaction) throws NxtException.ValidationException { - Attachment.DigitalGoodsFeedback attachment = (Attachment.DigitalGoodsFeedback) transaction.getAttachment(); - DigitalGoodsStore.Purchase purchase = DigitalGoodsStore.getPurchase(attachment.getPurchaseId()); - if (purchase != null && - (purchase.getSellerId() != transaction.getRecipientId() - || transaction.getSenderId() != purchase.getBuyerId())) { - throw new NxtException.NotValidException("Invalid digital goods feedback: " + attachment.getJSONObject()); - } - if (transaction.getEncryptedMessage() == null && transaction.getMessage() == null) { - throw new NxtException.NotValidException("Missing feedback message"); - } - if (transaction.getEncryptedMessage() != null && ! transaction.getEncryptedMessage().isText()) { - throw new NxtException.NotValidException("Only text encrypted messages allowed"); - } - if (transaction.getMessage() != null && ! transaction.getMessage().isText()) { - throw new NxtException.NotValidException("Only text public messages allowed"); - } - if (purchase == null || purchase.getEncryptedGoods() == null) { - throw new NxtException.NotCurrentlyValidException("Purchase does not exist yet or not yet delivered"); - } - } - - @Override - boolean isDuplicate(Transaction transaction, Map> duplicates) { - Attachment.DigitalGoodsFeedback attachment = (Attachment.DigitalGoodsFeedback) transaction.getAttachment(); - return isDuplicate(DigitalGoods.FEEDBACK, Convert.toUnsignedLong(attachment.getPurchaseId()), duplicates); - } - - @Override - public boolean hasRecipient() { - return true; - } - - }; - - public static final TransactionType REFUND = new DigitalGoods() { - - @Override - public final byte getSubtype() { - return TransactionType.SUBTYPE_DIGITAL_GOODS_REFUND; - } - - @Override - Attachment.DigitalGoodsRefund parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { - return new Attachment.DigitalGoodsRefund(buffer, transactionVersion); - } - - @Override - Attachment.DigitalGoodsRefund parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { - return new Attachment.DigitalGoodsRefund(attachmentData); - } - - @Override - boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { - Attachment.DigitalGoodsRefund attachment = (Attachment.DigitalGoodsRefund) transaction.getAttachment(); - if (senderAccount.getUnconfirmedBalanceNQT() >= attachment.getRefundNQT()) { - senderAccount.addToUnconfirmedBalanceNQT(-attachment.getRefundNQT()); - return true; - } - return false; - } - - @Override - void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { - Attachment.DigitalGoodsRefund attachment = (Attachment.DigitalGoodsRefund) transaction.getAttachment(); - senderAccount.addToUnconfirmedBalanceNQT(attachment.getRefundNQT()); - } - - @Override - void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { - Attachment.DigitalGoodsRefund attachment = (Attachment.DigitalGoodsRefund) transaction.getAttachment(); - DigitalGoodsStore.refund(transaction.getSenderId(), attachment.getPurchaseId(), - attachment.getRefundNQT(), transaction.getEncryptedMessage()); - } - - @Override - void doValidateAttachment(Transaction transaction) throws NxtException.ValidationException { - Attachment.DigitalGoodsRefund attachment = (Attachment.DigitalGoodsRefund) transaction.getAttachment(); - DigitalGoodsStore.Purchase purchase = DigitalGoodsStore.getPurchase(attachment.getPurchaseId()); - if (attachment.getRefundNQT() < 0 || attachment.getRefundNQT() > Constants.MAX_BALANCE_NQT - || (purchase != null && - (purchase.getBuyerId() != transaction.getRecipientId() - || transaction.getSenderId() != purchase.getSellerId()))) { - throw new NxtException.NotValidException("Invalid digital goods refund: " + attachment.getJSONObject()); - } - if (transaction.getEncryptedMessage() != null && ! transaction.getEncryptedMessage().isText()) { - throw new NxtException.NotValidException("Only text encrypted messages allowed"); - } - if (purchase == null || purchase.getEncryptedGoods() == null || purchase.getRefundNQT() != 0) { - throw new NxtException.NotCurrentlyValidException("Purchase does not exist or is not delivered or is already refunded"); - } - } - - @Override - boolean isDuplicate(Transaction transaction, Map> duplicates) { - Attachment.DigitalGoodsRefund attachment = (Attachment.DigitalGoodsRefund) transaction.getAttachment(); - return isDuplicate(DigitalGoods.REFUND, Convert.toUnsignedLong(attachment.getPurchaseId()), duplicates); - } - - @Override - public boolean hasRecipient() { - return true; - } - - }; - - } - - public static abstract class AccountControl extends TransactionType { - - private AccountControl() { - } - - @Override - public final byte getType() { - return TransactionType.TYPE_ACCOUNT_CONTROL; - } - - @Override - final boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { - return true; - } - - @Override - final void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { - } - - public static final TransactionType EFFECTIVE_BALANCE_LEASING = new AccountControl() { - - @Override - public final byte getSubtype() { - return TransactionType.SUBTYPE_ACCOUNT_CONTROL_EFFECTIVE_BALANCE_LEASING; - } - - @Override - Attachment.AccountControlEffectiveBalanceLeasing parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { - return new Attachment.AccountControlEffectiveBalanceLeasing(buffer, transactionVersion); - } - - @Override - Attachment.AccountControlEffectiveBalanceLeasing parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { - return new Attachment.AccountControlEffectiveBalanceLeasing(attachmentData); - } - - @Override - void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { - Attachment.AccountControlEffectiveBalanceLeasing attachment = (Attachment.AccountControlEffectiveBalanceLeasing) transaction.getAttachment(); - Account.getAccount(transaction.getSenderId()).leaseEffectiveBalance(transaction.getRecipientId(), attachment.getPeriod()); - } - - @Override - void validateAttachment(Transaction transaction) throws NxtException.ValidationException { - Attachment.AccountControlEffectiveBalanceLeasing attachment = (Attachment.AccountControlEffectiveBalanceLeasing)transaction.getAttachment(); - Account recipientAccount = Account.getAccount(transaction.getRecipientId()); - if (transaction.getSenderId() == transaction.getRecipientId() - || transaction.getAmountNQT() != 0 - || attachment.getPeriod() < 1440) { - throw new NxtException.NotValidException("Invalid effective balance leasing: " - + transaction.getJSONObject() + " transaction " + transaction.getStringId()); - } - if (recipientAccount == null - || (recipientAccount.getPublicKey() == null && ! transaction.getStringId().equals("5081403377391821646"))) { - throw new NxtException.NotCurrentlyValidException("Invalid effective balance leasing: " - + " recipient account " + transaction.getRecipientId() + " not found or no public key published"); - } - } - - @Override - public boolean hasRecipient() { - return true; - } - - }; - - } - - public static abstract class BurstMining extends TransactionType { - - private BurstMining() {} - - @Override - public final byte getType() { - return TransactionType.TYPE_BURST_MINING; - } - - @Override - final boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { - return true; - } - - @Override - final void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) {} - - public static final TransactionType REWARD_RECIPIENT_ASSIGNMENT = new BurstMining() { - - @Override - public final byte getSubtype() { - return TransactionType.SUBTYPE_BURST_MINING_REWARD_RECIPIENT_ASSIGNMENT; - } - - @Override - Attachment.BurstMiningRewardRecipientAssignment parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { - return new Attachment.BurstMiningRewardRecipientAssignment(buffer, transactionVersion); - } - - @Override - Attachment.BurstMiningRewardRecipientAssignment parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { - return new Attachment.BurstMiningRewardRecipientAssignment(attachmentData); - } - - @Override - void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { - senderAccount.setRewardRecipientAssignment(recipientAccount.getId()); - } - - @Override - boolean isDuplicate(Transaction transaction, Map> duplicates) { - if(Nxt.getBlockchain().getHeight() < Constants.DIGITAL_GOODS_STORE_BLOCK) { - return false; // sync fails after 7007 without this - } - return isDuplicate(BurstMining.REWARD_RECIPIENT_ASSIGNMENT, Convert.toUnsignedLong(transaction.getSenderId()), duplicates); - } - - @Override - void validateAttachment(Transaction transaction) throws NxtException.ValidationException { - long height = Nxt.getBlockchain().getLastBlock().getHeight() + 1; - Account sender = Account.getAccount(transaction.getSenderId()); - Account.RewardRecipientAssignment rewardAssignment = sender.getRewardRecipientAssignment(); - if(rewardAssignment != null && rewardAssignment.getFromHeight() >= height) { - throw new NxtException.NotValidException("Cannot reassign reward recipient before previous goes into effect: " + transaction.getJSONObject()); - } - Account recip = Account.getAccount(transaction.getRecipientId()); - if(recip == null || recip.getPublicKey() == null) { - throw new NxtException.NotValidException("Reward recipient must have public key saved in blockchain: " + transaction.getJSONObject()); - } - if(transaction.getAmountNQT() != 0 || transaction.getFeeNQT() != Constants.ONE_NXT) { - throw new NxtException.NotValidException("Reward recipient assisnment transaction must have 0 send amount and 1 fee: " + transaction.getJSONObject()); - } - if(height < Constants.BURST_REWARD_RECIPIENT_ASSIGNMENT_START_BLOCK) { - throw new NxtException.NotCurrentlyValidException("Reward recipient assignment not allowed before block " + Constants.BURST_REWARD_RECIPIENT_ASSIGNMENT_START_BLOCK); - } - } - - @Override - public boolean hasRecipient() { - return true; - } - }; - } - - public static abstract class AdvancedPayment extends TransactionType { - - private AdvancedPayment() {} - - @Override - public final byte getType() { - return TransactionType.TYPE_ADVANCED_PAYMENT; - } - - public final static TransactionType ESCROW_CREATION = new AdvancedPayment() { - - @Override - public final byte getSubtype() { - return TransactionType.SUBTYPE_ADVANCED_PAYMENT_ESCROW_CREATION; - } - - @Override - Attachment.AdvancedPaymentEscrowCreation parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { - return new Attachment.AdvancedPaymentEscrowCreation(buffer, transactionVersion); - } - - @Override - Attachment.AdvancedPaymentEscrowCreation parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { - return new Attachment.AdvancedPaymentEscrowCreation(attachmentData); - } - - @Override - final boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { - Attachment.AdvancedPaymentEscrowCreation attachment = (Attachment.AdvancedPaymentEscrowCreation) transaction.getAttachment(); - Long totalAmountNQT = Convert.safeAdd(attachment.getAmountNQT(), attachment.getTotalSigners() * Constants.ONE_NXT); - if(senderAccount.getUnconfirmedBalanceNQT() < totalAmountNQT.longValue()) { - return false; - } - senderAccount.addToUnconfirmedBalanceNQT(-totalAmountNQT); - return true; - } - - @Override - final void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { - Attachment.AdvancedPaymentEscrowCreation attachment = (Attachment.AdvancedPaymentEscrowCreation) transaction.getAttachment(); - Long totalAmountNQT = Convert.safeAdd(attachment.getAmountNQT(), attachment.getTotalSigners() * Constants.ONE_NXT); - senderAccount.addToBalanceNQT(-totalAmountNQT); - Collection signers = attachment.getSigners(); - for(Long signer : signers) { - Account.addOrGetAccount(signer).addToBalanceAndUnconfirmedBalanceNQT(Constants.ONE_NXT); - } - Escrow.addEscrowTransaction(senderAccount, - recipientAccount, - transaction.getId(), - attachment.getAmountNQT(), - attachment.getRequiredSigners(), - attachment.getSigners(), - transaction.getTimestamp() + attachment.getDeadline(), - attachment.getDeadlineAction()); - } - - @Override - final void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { - Attachment.AdvancedPaymentEscrowCreation attachment = (Attachment.AdvancedPaymentEscrowCreation) transaction.getAttachment(); - Long totalAmountNQT = Convert.safeAdd(attachment.getAmountNQT(), attachment.getTotalSigners() * Constants.ONE_NXT); - senderAccount.addToUnconfirmedBalanceNQT(totalAmountNQT); - } - - @Override - boolean isDuplicate(Transaction transaction, Map> duplicates) { - return false; - } - - @Override - void validateAttachment(Transaction transaction) throws NxtException.ValidationException { - Attachment.AdvancedPaymentEscrowCreation attachment = (Attachment.AdvancedPaymentEscrowCreation) transaction.getAttachment(); - Long totalAmountNQT = Convert.safeAdd(attachment.getAmountNQT(), transaction.getFeeNQT()); - if(transaction.getSenderId() == transaction.getRecipientId()) { - throw new NxtException.NotValidException("Escrow must have different sender and recipient"); - } - totalAmountNQT = Convert.safeAdd(totalAmountNQT, attachment.getTotalSigners() * Constants.ONE_NXT); - if(transaction.getAmountNQT() != 0) { - throw new NxtException.NotValidException("Transaction sent amount must be 0 for escrow"); - } - if(totalAmountNQT.compareTo(0L) < 0 || - totalAmountNQT.compareTo(Constants.MAX_BALANCE_NQT) > 0) - { - throw new NxtException.NotValidException("Invalid escrow creation amount"); - } - if(transaction.getFeeNQT() < Constants.ONE_NXT) { - throw new NxtException.NotValidException("Escrow transaction must have a fee at least 1 burst"); - } - if(attachment.getRequiredSigners() < 1 || attachment.getRequiredSigners() > 10) { - throw new NxtException.NotValidException("Escrow required signers much be 1 - 10"); - } - if(attachment.getRequiredSigners() > attachment.getTotalSigners()) { - throw new NxtException.NotValidException("Cannot have more required than signers on escrow"); - } - if(attachment.getTotalSigners() < 1 || attachment.getTotalSigners() > 10) { - throw new NxtException.NotValidException("Escrow transaction requires 1 - 10 signers"); - } - if(attachment.getDeadline() < 1 || attachment.getDeadline() > 7776000) { // max deadline 3 months - throw new NxtException.NotValidException("Escrow deadline must be 1 - 7776000 seconds"); - } - if(attachment.getDeadlineAction() == null || attachment.getDeadlineAction() == Escrow.DecisionType.UNDECIDED) { - throw new NxtException.NotValidException("Invalid deadline action for escrow"); - } - if(attachment.getSigners().contains(transaction.getSenderId()) || - attachment.getSigners().contains(transaction.getRecipientId())) { - throw new NxtException.NotValidException("Escrow sender and recipient cannot be signers"); - } - if(!Escrow.isEnabled()) { - throw new NxtException.NotYetEnabledException("Escrow not yet enabled"); - } - } - - @Override - final public boolean hasRecipient() { - return true; - } - }; - - public final static TransactionType ESCROW_SIGN = new AdvancedPayment() { - - @Override - public final byte getSubtype() { - return TransactionType.SUBTYPE_ADVANCED_PAYMENT_ESCROW_SIGN; - } - - @Override - Attachment.AdvancedPaymentEscrowSign parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { - return new Attachment.AdvancedPaymentEscrowSign(buffer, transactionVersion); - } - - @Override - Attachment.AdvancedPaymentEscrowSign parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { - return new Attachment.AdvancedPaymentEscrowSign(attachmentData); - } - - @Override - final boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { - return true; - } - - @Override - final void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { - Attachment.AdvancedPaymentEscrowSign attachment = (Attachment.AdvancedPaymentEscrowSign) transaction.getAttachment(); - Escrow escrow = Escrow.getEscrowTransaction(attachment.getEscrowId()); - escrow.sign(senderAccount.getId(), attachment.getDecision()); - } - - @Override - final void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { - } - - @Override - boolean isDuplicate(Transaction transaction, Map> duplicates) { - Attachment.AdvancedPaymentEscrowSign attachment = (Attachment.AdvancedPaymentEscrowSign) transaction.getAttachment(); - String uniqueString = Convert.toUnsignedLong(attachment.getEscrowId()) + ":" + - Convert.toUnsignedLong(transaction.getSenderId()); - return isDuplicate(AdvancedPayment.ESCROW_SIGN, uniqueString, duplicates); - } - - @Override - void validateAttachment(Transaction transaction) throws NxtException.ValidationException { - Attachment.AdvancedPaymentEscrowSign attachment = (Attachment.AdvancedPaymentEscrowSign) transaction.getAttachment(); - if(transaction.getAmountNQT() != 0 || transaction.getFeeNQT() != Constants.ONE_NXT) { - throw new NxtException.NotValidException("Escrow signing must have amount 0 and fee of 1"); - } - if(attachment.getEscrowId() == null || attachment.getDecision() == null) { - throw new NxtException.NotValidException("Escrow signing requires escrow id and decision set"); - } - Escrow escrow = Escrow.getEscrowTransaction(attachment.getEscrowId()); - if(escrow == null) { - throw new NxtException.NotValidException("Escrow transaction not found"); - } - if(!escrow.isIdSigner(transaction.getSenderId()) && - !escrow.getSenderId().equals(transaction.getSenderId()) && - !escrow.getRecipientId().equals(transaction.getSenderId())) { - throw new NxtException.NotValidException("Sender is not a participant in specified escrow"); - } - if(escrow.getSenderId().equals(transaction.getSenderId()) && attachment.getDecision() != Escrow.DecisionType.RELEASE) { - throw new NxtException.NotValidException("Escrow sender can only release"); - } - if(escrow.getRecipientId().equals(transaction.getSenderId()) && attachment.getDecision() != Escrow.DecisionType.REFUND) { - throw new NxtException.NotValidException("Escrow recipient can only refund"); - } - if(!Escrow.isEnabled()) { - throw new NxtException.NotYetEnabledException("Escrow not yet enabled"); - } - } - - @Override - final public boolean hasRecipient() { - return false; - } - }; - - public final static TransactionType ESCROW_RESULT = new AdvancedPayment() { - - @Override - public final byte getSubtype() { - return TransactionType.SUBTYPE_ADVANCED_PAYMENT_ESCROW_RESULT; - } - - @Override - Attachment.AdvancedPaymentEscrowResult parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { - return new Attachment.AdvancedPaymentEscrowResult(buffer, transactionVersion); - } - - @Override - Attachment.AdvancedPaymentEscrowResult parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { - return new Attachment.AdvancedPaymentEscrowResult(attachmentData); - } - - @Override - final boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { - return false; - } - - @Override - final void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { - } - - @Override - final void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { - } - - @Override - boolean isDuplicate(Transaction transaction, Map> duplicates) { - return true; - } - - @Override - void validateAttachment(Transaction transaction) throws NxtException.ValidationException { - throw new NxtException.NotValidException("Escrow result never validates"); - } - - @Override - final public boolean hasRecipient() { - return true; - } - - @Override - final public boolean isSigned() { - return false; - } - }; - - public final static TransactionType SUBSCRIPTION_SUBSCRIBE = new AdvancedPayment() { - - @Override - public final byte getSubtype() { - return TransactionType.SUBTYPE_ADVANCED_PAYMENT_SUBSCRIPTION_SUBSCRIBE; - } - - @Override - Attachment.AdvancedPaymentSubscriptionSubscribe parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { - return new Attachment.AdvancedPaymentSubscriptionSubscribe(buffer, transactionVersion); - } - - @Override - Attachment.AdvancedPaymentSubscriptionSubscribe parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { - return new Attachment.AdvancedPaymentSubscriptionSubscribe(attachmentData); - } - - @Override - final boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { - return true; - } - - @Override - final void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { - Attachment.AdvancedPaymentSubscriptionSubscribe attachment = (Attachment.AdvancedPaymentSubscriptionSubscribe) transaction.getAttachment(); - Subscription.addSubscription(senderAccount, recipientAccount, transaction.getId(), transaction.getAmountNQT(), transaction.getTimestamp(), attachment.getFrequency()); - } - - @Override - final void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { - } - - @Override - boolean isDuplicate(Transaction transaction, Map> duplicates) { - return false; - } - - @Override - void validateAttachment(Transaction transaction) throws NxtException.ValidationException { - Attachment.AdvancedPaymentSubscriptionSubscribe attachment = (Attachment.AdvancedPaymentSubscriptionSubscribe) transaction.getAttachment(); - if(attachment.getFrequency() == null || - attachment.getFrequency().intValue() < Constants.BURST_SUBSCRIPTION_MIN_FREQ || - attachment.getFrequency().intValue() > Constants.BURST_SUBSCRIPTION_MAX_FREQ) { - throw new NxtException.NotValidException("Invalid subscription frequency"); - } - if(transaction.getAmountNQT() < Constants.ONE_NXT || transaction.getAmountNQT() > Constants.MAX_BALANCE_NQT) { - throw new NxtException.NotValidException("Subscriptions must be at least one burst"); - } - if(transaction.getSenderId() == transaction.getRecipientId()) { - throw new NxtException.NotValidException("Cannot create subscription to same address"); - } - if(!Subscription.isEnabled()) { - throw new NxtException.NotYetEnabledException("Subscriptions not yet enabled"); - } - } - - @Override - final public boolean hasRecipient() { - return true; - } - }; - - public final static TransactionType SUBSCRIPTION_CANCEL = new AdvancedPayment() { - - @Override - public final byte getSubtype() { - return TransactionType.SUBTYPE_ADVANCED_PAYMENT_SUBSCRIPTION_CANCEL; - } - - @Override - Attachment.AdvancedPaymentSubscriptionCancel parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { - return new Attachment.AdvancedPaymentSubscriptionCancel(buffer, transactionVersion); - } - - @Override - Attachment.AdvancedPaymentSubscriptionCancel parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { - return new Attachment.AdvancedPaymentSubscriptionCancel(attachmentData); - } - - @Override - final boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { - Attachment.AdvancedPaymentSubscriptionCancel attachment = (Attachment.AdvancedPaymentSubscriptionCancel) transaction.getAttachment(); - Subscription.addRemoval(attachment.getSubscriptionId()); - return true; - } - - @Override - final void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { - Attachment.AdvancedPaymentSubscriptionCancel attachment = (Attachment.AdvancedPaymentSubscriptionCancel) transaction.getAttachment(); - Subscription.removeSubscription(attachment.getSubscriptionId()); - } - - @Override - final void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { - } - - @Override - boolean isDuplicate(Transaction transaction, Map> duplicates) { - Attachment.AdvancedPaymentSubscriptionCancel attachment = (Attachment.AdvancedPaymentSubscriptionCancel) transaction.getAttachment(); - return isDuplicate(AdvancedPayment.SUBSCRIPTION_CANCEL, Convert.toUnsignedLong(attachment.getSubscriptionId()), duplicates); - } - - @Override - void validateAttachment(Transaction transaction) throws NxtException.ValidationException { - Attachment.AdvancedPaymentSubscriptionCancel attachment = (Attachment.AdvancedPaymentSubscriptionCancel) transaction.getAttachment(); - if(attachment.getSubscriptionId() == null) { - throw new NxtException.NotValidException("Subscription cancel must include subscription id"); - } - - Subscription subscription = Subscription.getSubscription(attachment.getSubscriptionId()); - if(subscription == null) { - throw new NxtException.NotValidException("Subscription cancel must contain current subscription id"); - } - - if(!subscription.getSenderId().equals(transaction.getSenderId()) && - !subscription.getRecipientId().equals(transaction.getSenderId())) { - throw new NxtException.NotValidException("Subscription cancel can only be done by participants"); - } - - if(!Subscription.isEnabled()) { - throw new NxtException.NotYetEnabledException("Subscription cancel not yet enabled"); - } - } - - @Override - final public boolean hasRecipient() { - return false; - } - }; - - public final static TransactionType SUBSCRIPTION_PAYMENT = new AdvancedPayment() { - - @Override - public final byte getSubtype() { - return TransactionType.SUBTYPE_ADVANCED_PAYMENT_SUBSCRIPTION_PAYMENT; - } - - @Override - Attachment.AdvancedPaymentSubscriptionPayment parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { - return new Attachment.AdvancedPaymentSubscriptionPayment(buffer, transactionVersion); - } - - @Override - Attachment.AdvancedPaymentSubscriptionPayment parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { - return new Attachment.AdvancedPaymentSubscriptionPayment(attachmentData); - } - - @Override - final boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { - return false; - } - - @Override - final void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { - } - - @Override - final void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { - } - - @Override - boolean isDuplicate(Transaction transaction, Map> duplicates) { - return true; - } - - @Override - void validateAttachment(Transaction transaction) throws NxtException.ValidationException { - throw new NxtException.NotValidException("Subscription payment never validates"); - } - - @Override - final public boolean hasRecipient() { - return true; - } - - @Override - final public boolean isSigned() { - return false; - } - }; - } - - public static abstract class AutomatedTransactions extends TransactionType{ - private AutomatedTransactions() { - - } - - @Override - public final byte getType(){ - return TransactionType.TYPE_AUTOMATED_TRANSACTIONS; - } - - @Override - boolean applyAttachmentUnconfirmed(Transaction transaction,Account senderAccount){ - return true; - } - - @Override - void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount){ - - } - - @Override - final void validateAttachment(Transaction transaction) throws NxtException.ValidationException { - if (transaction.getAmountNQT() != 0) { - throw new NxtException.NotValidException("Invalid automated transaction transaction"); - } - doValidateAttachment(transaction); - } - - abstract void doValidateAttachment(Transaction transaction) throws NxtException.ValidationException; - - - public static final TransactionType AUTOMATED_TRANSACTION_CREATION = new AutomatedTransactions(){ - - @Override - public byte getSubtype() { - return TransactionType.SUBTYPE_AT_CREATION; - } - - @Override - AbstractAttachment parseAttachment(ByteBuffer buffer, - byte transactionVersion) throws NotValidException { - // TODO Auto-generated method stub - //System.out.println("parsing byte AT attachment"); - AutomatedTransactionsCreation attachment = new Attachment.AutomatedTransactionsCreation(buffer,transactionVersion); - //System.out.println("byte AT attachment parsed"); - return attachment; - } - - @Override - AbstractAttachment parseAttachment(JSONObject attachmentData) - throws NotValidException { - // TODO Auto-generated method stub - //System.out.println("parsing at attachment"); - Attachment.AutomatedTransactionsCreation atCreateAttachment = new Attachment.AutomatedTransactionsCreation(attachmentData); - //System.out.println("attachment parsed"); - return atCreateAttachment; - } - - @Override - void doValidateAttachment(Transaction transaction) - throws ValidationException { - //System.out.println("validating attachment"); - if (Nxt.getBlockchain().getLastBlock().getHeight()< Constants.AUTOMATED_TRANSACTION_BLOCK){ - throw new NxtException.NotYetEnabledException("Automated Transactions not yet enabled at height " + Nxt.getBlockchain().getLastBlock().getHeight()); - } - if (transaction.getSignature() != null && Account.getAccount(transaction.getId()) != null) { - Account existingAccount = Account.getAccount(transaction.getId()); - if(existingAccount.getPublicKey() != null && !Arrays.equals(existingAccount.getPublicKey(), new byte[32])) - throw new NxtException.NotValidException("Account with id already exists"); - } - Attachment.AutomatedTransactionsCreation attachment = (Attachment.AutomatedTransactionsCreation) transaction.getAttachment(); - long totalPages = 0; - try { - totalPages = AT_Controller.checkCreationBytes(attachment.getCreationBytes(), Nxt.getBlockchain().getHeight()); - } - catch(AT_Exception e) { - throw new NxtException.NotCurrentlyValidException("Invalid AT creation bytes", e); - } - long requiredFee = totalPages * AT_Constants.getInstance().COST_PER_PAGE( transaction.getHeight() ); - if (transaction.getFeeNQT() < requiredFee){ - throw new NxtException.NotValidException("Insufficient fee for AT creation. Minimum: " + Convert.toUnsignedLong(requiredFee / Constants.ONE_NXT)); - } - if(Nxt.getBlockchain().getHeight() >= Constants.AT_FIX_BLOCK_3) { - if(attachment.getName().length() > Constants.MAX_AUTOMATED_TRANSACTION_NAME_LENGTH) { - throw new NxtException.NotValidException("Name of automated transaction over size limit"); - } - if(attachment.getDescription().length() > Constants.MAX_AUTOMATED_TRANSACTION_DESCRIPTION_LENGTH) { - throw new NxtException.NotValidException("Description of automated transaction over size limit"); - } - } - //System.out.println("validating success"); - } - - @Override - void applyAttachment(Transaction transaction, - Account senderAccount, Account recipientAccount) { - // TODO Auto-generated method stub - Attachment.AutomatedTransactionsCreation attachment = (Attachment.AutomatedTransactionsCreation) transaction.getAttachment(); - Long atId = transaction.getId(); - //System.out.println("Applying AT attachent"); - AT.addAT( transaction.getId() , transaction.getSenderId() , attachment.getName() , attachment.getDescription() , attachment.getCreationBytes() , transaction.getHeight() ); - //System.out.println("At with id "+atId+" successfully applied"); - } - - - @Override - public boolean hasRecipient() { - // TODO Auto-generated method stub - return false; - } - }; - - public static final TransactionType AT_PAYMENT = new AutomatedTransactions() { - @Override - public final byte getSubtype() { - return TransactionType.SUBTYPE_AT_NXT_PAYMENT; - } - - @Override - AbstractAttachment parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { - return Attachment.AT_PAYMENT; - } - - @Override - AbstractAttachment parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { - return Attachment.AT_PAYMENT; - } - - @Override - void doValidateAttachment(Transaction transaction) throws NxtException.ValidationException { - /*if (transaction.getAmountNQT() <= 0 || transaction.getAmountNQT() >= Constants.MAX_BALANCE_NQT) { - throw new NxtException.NotValidException("Invalid ordinary payment"); - }*/ - throw new NxtException.NotValidException("AT payment never validates"); - } - - @Override - void applyAttachment(Transaction transaction, - Account senderAccount, Account recipientAccount) { - // TODO Auto-generated method stub - - } - - - @Override - public boolean hasRecipient() { - return true; - } - - @Override - final public boolean isSigned() { - return false; - } - }; - - } - - long minimumFeeNQT(int height, int appendagesSize) { - if (height < BASELINE_FEE_HEIGHT) { - return 0; // No need to validate fees before baseline block - } - Fee fee; - if (height >= NEXT_FEE_HEIGHT) { - fee = getNextFee(); - } else { - fee = getBaselineFee(); - } - return Convert.safeAdd(fee.getConstantFee(), Convert.safeMultiply(appendagesSize, fee.getAppendagesFee())); - } - - protected Fee getBaselineFee() { - return BASELINE_FEE; - } - - protected Fee getNextFee() { - return NEXT_FEE; - } - - public static final class Fee { - private final long constantFee; - private final long appendagesFee; - - public Fee(long constantFee, long appendagesFee) { - this.constantFee = constantFee; - this.appendagesFee = appendagesFee; - } - - public long getConstantFee() { - return constantFee; - } - - public long getAppendagesFee() { - return appendagesFee; - } - - @Override - public String toString() { - return "Fee{" + - "constantFee=" + constantFee + - ", appendagesFee=" + appendagesFee + - '}'; - } - } - -} +package nxt; + +import nxt.Attachment.AbstractAttachment; +import nxt.Attachment.AutomatedTransactionsCreation; +import nxt.NxtException.NotValidException; +import nxt.NxtException.ValidationException; +import nxt.at.AT_Constants; +import nxt.at.AT_Controller; +import nxt.at.AT_Exception; +import nxt.util.Convert; + +import org.json.simple.JSONObject; + +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + + +public abstract class TransactionType { + + private static final byte TYPE_PAYMENT = 0; + private static final byte TYPE_MESSAGING = 1; + private static final byte TYPE_COLORED_COINS = 2; + private static final byte TYPE_DIGITAL_GOODS = 3; + private static final byte TYPE_ACCOUNT_CONTROL = 4; + + private static final byte TYPE_BURST_MINING = 20; // jump some for easier nxt updating + private static final byte TYPE_ADVANCED_PAYMENT = 21; + private static final byte TYPE_AUTOMATED_TRANSACTIONS = 22; + + private static final byte SUBTYPE_PAYMENT_ORDINARY_PAYMENT = 0; + + private static final byte SUBTYPE_MESSAGING_ARBITRARY_MESSAGE = 0; + private static final byte SUBTYPE_MESSAGING_ALIAS_ASSIGNMENT = 1; + private static final byte SUBTYPE_MESSAGING_POLL_CREATION = 2; + private static final byte SUBTYPE_MESSAGING_VOTE_CASTING = 3; + private static final byte SUBTYPE_MESSAGING_HUB_ANNOUNCEMENT = 4; + private static final byte SUBTYPE_MESSAGING_ACCOUNT_INFO = 5; + private static final byte SUBTYPE_MESSAGING_ALIAS_SELL = 6; + private static final byte SUBTYPE_MESSAGING_ALIAS_BUY = 7; + + private static final byte SUBTYPE_COLORED_COINS_ASSET_ISSUANCE = 0; + private static final byte SUBTYPE_COLORED_COINS_ASSET_TRANSFER = 1; + private static final byte SUBTYPE_COLORED_COINS_ASK_ORDER_PLACEMENT = 2; + private static final byte SUBTYPE_COLORED_COINS_BID_ORDER_PLACEMENT = 3; + private static final byte SUBTYPE_COLORED_COINS_ASK_ORDER_CANCELLATION = 4; + private static final byte SUBTYPE_COLORED_COINS_BID_ORDER_CANCELLATION = 5; + + private static final byte SUBTYPE_DIGITAL_GOODS_LISTING = 0; + private static final byte SUBTYPE_DIGITAL_GOODS_DELISTING = 1; + private static final byte SUBTYPE_DIGITAL_GOODS_PRICE_CHANGE = 2; + private static final byte SUBTYPE_DIGITAL_GOODS_QUANTITY_CHANGE = 3; + private static final byte SUBTYPE_DIGITAL_GOODS_PURCHASE = 4; + private static final byte SUBTYPE_DIGITAL_GOODS_DELIVERY = 5; + private static final byte SUBTYPE_DIGITAL_GOODS_FEEDBACK = 6; + private static final byte SUBTYPE_DIGITAL_GOODS_REFUND = 7; + + private static final byte SUBTYPE_AT_CREATION = 0; + private static final byte SUBTYPE_AT_NXT_PAYMENT = 1; + + private static final byte SUBTYPE_ACCOUNT_CONTROL_EFFECTIVE_BALANCE_LEASING = 0; + + private static final byte SUBTYPE_BURST_MINING_REWARD_RECIPIENT_ASSIGNMENT = 0; + + private static final byte SUBTYPE_ADVANCED_PAYMENT_ESCROW_CREATION = 0; + private static final byte SUBTYPE_ADVANCED_PAYMENT_ESCROW_SIGN = 1; + private static final byte SUBTYPE_ADVANCED_PAYMENT_ESCROW_RESULT = 2; + private static final byte SUBTYPE_ADVANCED_PAYMENT_SUBSCRIPTION_SUBSCRIBE = 3; + private static final byte SUBTYPE_ADVANCED_PAYMENT_SUBSCRIPTION_CANCEL = 4; + private static final byte SUBTYPE_ADVANCED_PAYMENT_SUBSCRIPTION_PAYMENT = 5; + + private static final int BASELINE_FEE_HEIGHT = 1; // At release time must be less than current block - 1440 + private static final Fee BASELINE_FEE = new Fee(Constants.ONE_NXT, 0); + private static final Fee BASELINE_ASSET_ISSUANCE_FEE = new Fee(1000 * Constants.ONE_NXT, 0); + private static final int NEXT_FEE_HEIGHT = Integer.MAX_VALUE; + private static final Fee NEXT_FEE = new Fee(Constants.ONE_NXT, 0); + private static final Fee NEXT_ASSET_ISSUANCE_FEE = new Fee(1000 * Constants.ONE_NXT, 0); + + public static TransactionType findTransactionType(byte type, byte subtype) { + switch (type) { + case TYPE_PAYMENT: + switch (subtype) { + case SUBTYPE_PAYMENT_ORDINARY_PAYMENT: + return Payment.ORDINARY; + default: + return null; + } + case TYPE_MESSAGING: + switch (subtype) { + case SUBTYPE_MESSAGING_ARBITRARY_MESSAGE: + return Messaging.ARBITRARY_MESSAGE; + case SUBTYPE_MESSAGING_ALIAS_ASSIGNMENT: + return Messaging.ALIAS_ASSIGNMENT; + case SUBTYPE_MESSAGING_POLL_CREATION: + return Messaging.POLL_CREATION; + case SUBTYPE_MESSAGING_VOTE_CASTING: + return Messaging.VOTE_CASTING; + case SUBTYPE_MESSAGING_HUB_ANNOUNCEMENT: + return Messaging.HUB_ANNOUNCEMENT; + case SUBTYPE_MESSAGING_ACCOUNT_INFO: + return Messaging.ACCOUNT_INFO; + case SUBTYPE_MESSAGING_ALIAS_SELL: + return Messaging.ALIAS_SELL; + case SUBTYPE_MESSAGING_ALIAS_BUY: + return Messaging.ALIAS_BUY; + default: + return null; + } + case TYPE_COLORED_COINS: + switch (subtype) { + case SUBTYPE_COLORED_COINS_ASSET_ISSUANCE: + return ColoredCoins.ASSET_ISSUANCE; + case SUBTYPE_COLORED_COINS_ASSET_TRANSFER: + return ColoredCoins.ASSET_TRANSFER; + case SUBTYPE_COLORED_COINS_ASK_ORDER_PLACEMENT: + return ColoredCoins.ASK_ORDER_PLACEMENT; + case SUBTYPE_COLORED_COINS_BID_ORDER_PLACEMENT: + return ColoredCoins.BID_ORDER_PLACEMENT; + case SUBTYPE_COLORED_COINS_ASK_ORDER_CANCELLATION: + return ColoredCoins.ASK_ORDER_CANCELLATION; + case SUBTYPE_COLORED_COINS_BID_ORDER_CANCELLATION: + return ColoredCoins.BID_ORDER_CANCELLATION; + default: + return null; + } + case TYPE_DIGITAL_GOODS: + switch (subtype) { + case SUBTYPE_DIGITAL_GOODS_LISTING: + return DigitalGoods.LISTING; + case SUBTYPE_DIGITAL_GOODS_DELISTING: + return DigitalGoods.DELISTING; + case SUBTYPE_DIGITAL_GOODS_PRICE_CHANGE: + return DigitalGoods.PRICE_CHANGE; + case SUBTYPE_DIGITAL_GOODS_QUANTITY_CHANGE: + return DigitalGoods.QUANTITY_CHANGE; + case SUBTYPE_DIGITAL_GOODS_PURCHASE: + return DigitalGoods.PURCHASE; + case SUBTYPE_DIGITAL_GOODS_DELIVERY: + return DigitalGoods.DELIVERY; + case SUBTYPE_DIGITAL_GOODS_FEEDBACK: + return DigitalGoods.FEEDBACK; + case SUBTYPE_DIGITAL_GOODS_REFUND: + return DigitalGoods.REFUND; + default: + return null; + } + case TYPE_ACCOUNT_CONTROL: + switch (subtype) { + case SUBTYPE_ACCOUNT_CONTROL_EFFECTIVE_BALANCE_LEASING: + return AccountControl.EFFECTIVE_BALANCE_LEASING; + default: + return null; + } + case TYPE_BURST_MINING: + switch (subtype) { + case SUBTYPE_BURST_MINING_REWARD_RECIPIENT_ASSIGNMENT: + return BurstMining.REWARD_RECIPIENT_ASSIGNMENT; + default: + return null; + } + case TYPE_ADVANCED_PAYMENT: + switch (subtype) { + case SUBTYPE_ADVANCED_PAYMENT_ESCROW_CREATION: + return AdvancedPayment.ESCROW_CREATION; + case SUBTYPE_ADVANCED_PAYMENT_ESCROW_SIGN: + return AdvancedPayment.ESCROW_SIGN; + case SUBTYPE_ADVANCED_PAYMENT_ESCROW_RESULT: + return AdvancedPayment.ESCROW_RESULT; + case SUBTYPE_ADVANCED_PAYMENT_SUBSCRIPTION_SUBSCRIBE: + return AdvancedPayment.SUBSCRIPTION_SUBSCRIBE; + case SUBTYPE_ADVANCED_PAYMENT_SUBSCRIPTION_CANCEL: + return AdvancedPayment.SUBSCRIPTION_CANCEL; + case SUBTYPE_ADVANCED_PAYMENT_SUBSCRIPTION_PAYMENT: + return AdvancedPayment.SUBSCRIPTION_PAYMENT; + default: + return null; + } + case TYPE_AUTOMATED_TRANSACTIONS: + switch (subtype) { + case SUBTYPE_AT_CREATION: + return AutomatedTransactions.AUTOMATED_TRANSACTION_CREATION; + case SUBTYPE_AT_NXT_PAYMENT: + return AutomatedTransactions.AT_PAYMENT; + default: + return null; + } + default: + return null; + } + } + + private TransactionType() { + } + + public abstract byte getType(); + + public abstract byte getSubtype(); + + abstract Attachment.AbstractAttachment parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException; + + abstract Attachment.AbstractAttachment parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException; + + abstract void validateAttachment(Transaction transaction) throws NxtException.ValidationException; + + // return false iff double spending + final boolean applyUnconfirmed(Transaction transaction, Account senderAccount) { + long totalAmountNQT = Convert.safeAdd(transaction.getAmountNQT(), transaction.getFeeNQT()); + if (transaction.getReferencedTransactionFullHash() != null + && transaction.getTimestamp() > Constants.REFERENCED_TRANSACTION_FULL_HASH_BLOCK_TIMESTAMP) { + totalAmountNQT = Convert.safeAdd(totalAmountNQT, Constants.UNCONFIRMED_POOL_DEPOSIT_NQT); + } + if (senderAccount.getUnconfirmedBalanceNQT() < totalAmountNQT) { + return false; + } + senderAccount.addToUnconfirmedBalanceNQT(-totalAmountNQT); + if (!applyAttachmentUnconfirmed(transaction, senderAccount)) { + senderAccount.addToUnconfirmedBalanceNQT(totalAmountNQT); + return false; + } + return true; + } + + abstract boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount); + + final void apply(Transaction transaction, Account senderAccount, Account recipientAccount) { + senderAccount.addToBalanceNQT(- (Convert.safeAdd(transaction.getAmountNQT(), transaction.getFeeNQT()))); + if (transaction.getReferencedTransactionFullHash() != null + && transaction.getTimestamp() > Constants.REFERENCED_TRANSACTION_FULL_HASH_BLOCK_TIMESTAMP) { + senderAccount.addToUnconfirmedBalanceNQT(Constants.UNCONFIRMED_POOL_DEPOSIT_NQT); + } + if (recipientAccount != null) { + recipientAccount.addToBalanceAndUnconfirmedBalanceNQT(transaction.getAmountNQT()); + } + applyAttachment(transaction, senderAccount, recipientAccount); + } + + abstract void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount); + + final void undoUnconfirmed(Transaction transaction, Account senderAccount) { + undoAttachmentUnconfirmed(transaction, senderAccount); + senderAccount.addToUnconfirmedBalanceNQT(Convert.safeAdd(transaction.getAmountNQT(), transaction.getFeeNQT())); + if (transaction.getReferencedTransactionFullHash() != null + && transaction.getTimestamp() > Constants.REFERENCED_TRANSACTION_FULL_HASH_BLOCK_TIMESTAMP) { + senderAccount.addToUnconfirmedBalanceNQT(Constants.UNCONFIRMED_POOL_DEPOSIT_NQT); + } + } + + abstract void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount); + + boolean isDuplicate(Transaction transaction, Map> duplicates) { + return false; + } + + static boolean isDuplicate(TransactionType uniqueType, String key, Map> duplicates) { + Set typeDuplicates = duplicates.get(uniqueType); + if (typeDuplicates == null) { + typeDuplicates = new HashSet<>(); + duplicates.put(uniqueType, typeDuplicates); + } + return ! typeDuplicates.add(key); + } + + public abstract boolean hasRecipient(); + + public boolean isSigned() { + return true; + } + + @Override + public final String toString() { + return "type: " + getType() + ", subtype: " + getSubtype(); + } + + /* + Collection getPhasingTransactionTypes() { + return Collections.emptyList(); + } + + Collection getPhasedTransactionTypes() { + return Collections.emptyList(); + } + */ + + public static abstract class Payment extends TransactionType { + + private Payment() { + } + + @Override + public final byte getType() { + return TransactionType.TYPE_PAYMENT; + } + + @Override + final boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { + return true; + } + + @Override + final void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { + } + + @Override + final void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { + } + + @Override + final public boolean hasRecipient() { + return true; + } + + public static final TransactionType ORDINARY = new Payment() { + + @Override + public final byte getSubtype() { + return TransactionType.SUBTYPE_PAYMENT_ORDINARY_PAYMENT; + } + + @Override + Attachment.EmptyAttachment parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { + return Attachment.ORDINARY_PAYMENT; + } + + @Override + Attachment.EmptyAttachment parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { + return Attachment.ORDINARY_PAYMENT; + } + + @Override + void validateAttachment(Transaction transaction) throws NxtException.ValidationException { + if (transaction.getAmountNQT() <= 0 || transaction.getAmountNQT() >= Constants.MAX_BALANCE_NQT) { + throw new NxtException.NotValidException("Invalid ordinary payment"); + } + } + + }; + + } + + public static abstract class Messaging extends TransactionType { + + private Messaging() { + } + + @Override + public final byte getType() { + return TransactionType.TYPE_MESSAGING; + } + + @Override + final boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { + return true; + } + + @Override + final void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { + } + + public final static TransactionType ARBITRARY_MESSAGE = new Messaging() { + + @Override + public final byte getSubtype() { + return TransactionType.SUBTYPE_MESSAGING_ARBITRARY_MESSAGE; + } + + @Override + Attachment.EmptyAttachment parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { + return Attachment.ARBITRARY_MESSAGE; + } + + @Override + Attachment.EmptyAttachment parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { + return Attachment.ARBITRARY_MESSAGE; + } + + @Override + void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { + } + + @Override + void validateAttachment(Transaction transaction) throws NxtException.ValidationException { + Attachment attachment = transaction.getAttachment(); + if (transaction.getAmountNQT() != 0) { + throw new NxtException.NotValidException("Invalid arbitrary message: " + attachment.getJSONObject()); + } + if (Nxt.getBlockchain().getHeight() < Constants.DIGITAL_GOODS_STORE_BLOCK && transaction.getMessage() == null) { + throw new NxtException.NotCurrentlyValidException("Missing message appendix not allowed before DGS block"); + } + } + + @Override + public boolean hasRecipient() { + return true; + } + + }; + + public static final TransactionType ALIAS_ASSIGNMENT = new Messaging() { + + @Override + public final byte getSubtype() { + return TransactionType.SUBTYPE_MESSAGING_ALIAS_ASSIGNMENT; + } + + @Override + Attachment.MessagingAliasAssignment parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { + return new Attachment.MessagingAliasAssignment(buffer, transactionVersion); + } + + @Override + Attachment.MessagingAliasAssignment parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { + return new Attachment.MessagingAliasAssignment(attachmentData); + } + + @Override + void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { + Attachment.MessagingAliasAssignment attachment = (Attachment.MessagingAliasAssignment) transaction.getAttachment(); + Alias.addOrUpdateAlias(transaction, attachment); + } + + @Override + boolean isDuplicate(Transaction transaction, Map> duplicates) { + Attachment.MessagingAliasAssignment attachment = (Attachment.MessagingAliasAssignment) transaction.getAttachment(); + return isDuplicate(Messaging.ALIAS_ASSIGNMENT, attachment.getAliasName().toLowerCase(), duplicates); + } + + @Override + void validateAttachment(Transaction transaction) throws NxtException.ValidationException { + Attachment.MessagingAliasAssignment attachment = (Attachment.MessagingAliasAssignment) transaction.getAttachment(); + if (attachment.getAliasName().length() == 0 + || attachment.getAliasName().length() > Constants.MAX_ALIAS_LENGTH + || attachment.getAliasURI().length() > Constants.MAX_ALIAS_URI_LENGTH) { + throw new NxtException.NotValidException("Invalid alias assignment: " + attachment.getJSONObject()); + } + String normalizedAlias = attachment.getAliasName().toLowerCase(); + for (int i = 0; i < normalizedAlias.length(); i++) { + if (Constants.ALPHABET.indexOf(normalizedAlias.charAt(i)) < 0) { + throw new NxtException.NotValidException("Invalid alias name: " + normalizedAlias); + } + } + Alias alias = Alias.getAlias(normalizedAlias); + if (alias != null && alias.getAccountId() != transaction.getSenderId()) { + throw new NxtException.NotCurrentlyValidException("Alias already owned by another account: " + normalizedAlias); + } + } + + @Override + public boolean hasRecipient() { + return false; + } + + }; + + public static final TransactionType ALIAS_SELL = new Messaging() { + + @Override + public final byte getSubtype() { + return TransactionType.SUBTYPE_MESSAGING_ALIAS_SELL; + } + + @Override + Attachment.MessagingAliasSell parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { + return new Attachment.MessagingAliasSell(buffer, transactionVersion); + } + + @Override + Attachment.MessagingAliasSell parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { + return new Attachment.MessagingAliasSell(attachmentData); + } + + @Override + void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { + final Attachment.MessagingAliasSell attachment = + (Attachment.MessagingAliasSell) transaction.getAttachment(); + Alias.sellAlias(transaction, attachment); + } + + @Override + boolean isDuplicate(Transaction transaction, Map> duplicates) { + Attachment.MessagingAliasSell attachment = (Attachment.MessagingAliasSell) transaction.getAttachment(); + // not a bug, uniqueness is based on Messaging.ALIAS_ASSIGNMENT + return isDuplicate(Messaging.ALIAS_ASSIGNMENT, attachment.getAliasName().toLowerCase(), duplicates); + } + + @Override + void validateAttachment(Transaction transaction) throws NxtException.ValidationException { + if (Nxt.getBlockchain().getLastBlock().getHeight() < Constants.DIGITAL_GOODS_STORE_BLOCK) { + throw new NxtException.NotYetEnabledException("Alias transfer not yet enabled at height " + Nxt.getBlockchain().getLastBlock().getHeight()); + } + if (transaction.getAmountNQT() != 0) { + throw new NxtException.NotValidException("Invalid sell alias transaction: " + + transaction.getJSONObject()); + } + final Attachment.MessagingAliasSell attachment = + (Attachment.MessagingAliasSell) transaction.getAttachment(); + final String aliasName = attachment.getAliasName(); + if (aliasName == null || aliasName.length() == 0) { + throw new NxtException.NotValidException("Missing alias name"); + } + long priceNQT = attachment.getPriceNQT(); + if (priceNQT < 0 || priceNQT > Constants.MAX_BALANCE_NQT) { + throw new NxtException.NotValidException("Invalid alias sell price: " + priceNQT); + } + if (priceNQT == 0) { + if (Genesis.CREATOR_ID == transaction.getRecipientId()) { + throw new NxtException.NotValidException("Transferring aliases to Genesis account not allowed"); + } else if (transaction.getRecipientId() == 0) { + throw new NxtException.NotValidException("Missing alias transfer recipient"); + } + } + final Alias alias = Alias.getAlias(aliasName); + if (alias == null) { + throw new NxtException.NotCurrentlyValidException("Alias hasn't been registered yet: " + aliasName); + } else if (alias.getAccountId() != transaction.getSenderId()) { + throw new NxtException.NotCurrentlyValidException("Alias doesn't belong to sender: " + aliasName); + } + } + + @Override + public boolean hasRecipient() { + return true; + } + + }; + + public static final TransactionType ALIAS_BUY = new Messaging() { + + @Override + public final byte getSubtype() { + return TransactionType.SUBTYPE_MESSAGING_ALIAS_BUY; + } + + @Override + Attachment.MessagingAliasBuy parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { + return new Attachment.MessagingAliasBuy(buffer, transactionVersion); + } + + @Override + Attachment.MessagingAliasBuy parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { + return new Attachment.MessagingAliasBuy(attachmentData); + } + + @Override + void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { + final Attachment.MessagingAliasBuy attachment = + (Attachment.MessagingAliasBuy) transaction.getAttachment(); + final String aliasName = attachment.getAliasName(); + Alias.changeOwner(transaction.getSenderId(), aliasName, transaction.getBlockTimestamp()); + } + + @Override + boolean isDuplicate(Transaction transaction, Map> duplicates) { + Attachment.MessagingAliasBuy attachment = (Attachment.MessagingAliasBuy) transaction.getAttachment(); + // not a bug, uniqueness is based on Messaging.ALIAS_ASSIGNMENT + return isDuplicate(Messaging.ALIAS_ASSIGNMENT, attachment.getAliasName().toLowerCase(), duplicates); + } + + @Override + void validateAttachment(Transaction transaction) throws NxtException.ValidationException { + if (Nxt.getBlockchain().getLastBlock().getHeight() < Constants.DIGITAL_GOODS_STORE_BLOCK) { + throw new NxtException.NotYetEnabledException("Alias transfer not yet enabled at height " + Nxt.getBlockchain().getLastBlock().getHeight()); + } + final Attachment.MessagingAliasBuy attachment = + (Attachment.MessagingAliasBuy) transaction.getAttachment(); + final String aliasName = attachment.getAliasName(); + final Alias alias = Alias.getAlias(aliasName); + if (alias == null) { + throw new NxtException.NotCurrentlyValidException("Alias hasn't been registered yet: " + aliasName); + } else if (alias.getAccountId() != transaction.getRecipientId()) { + throw new NxtException.NotCurrentlyValidException("Alias is owned by account other than recipient: " + + Convert.toUnsignedLong(alias.getAccountId())); + } + Alias.Offer offer = Alias.getOffer(alias); + if (offer == null) { + throw new NxtException.NotCurrentlyValidException("Alias is not for sale: " + aliasName); + } + if (transaction.getAmountNQT() < offer.getPriceNQT()) { + String msg = "Price is too low for: " + aliasName + " (" + + transaction.getAmountNQT() + " < " + offer.getPriceNQT() + ")"; + throw new NxtException.NotCurrentlyValidException(msg); + } + if (offer.getBuyerId() != 0 && offer.getBuyerId() != transaction.getSenderId()) { + throw new NxtException.NotCurrentlyValidException("Wrong buyer for " + aliasName + ": " + + Convert.toUnsignedLong(transaction.getSenderId()) + " expected: " + + Convert.toUnsignedLong(offer.getBuyerId())); + } + } + + @Override + public boolean hasRecipient() { + return true; + } + + }; + + public final static TransactionType POLL_CREATION = new Messaging() { + @Override + public final byte getSubtype() { + return TransactionType.SUBTYPE_MESSAGING_POLL_CREATION; + } + + @Override + Attachment.MessagingPollCreation parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { + return new Attachment.MessagingPollCreation(buffer, transactionVersion); + } + + @Override + Attachment.MessagingPollCreation parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { + return new Attachment.MessagingPollCreation(attachmentData); + } + + @Override + void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { + Attachment.MessagingPollCreation attachment = (Attachment.MessagingPollCreation) transaction.getAttachment(); + Poll.addPoll(transaction, attachment); + } + + @Override + void validateAttachment(Transaction transaction) throws NxtException.ValidationException { + if (Nxt.getBlockchain().getLastBlock().getHeight() < Constants.VOTING_SYSTEM_BLOCK) { + throw new NxtException.NotYetEnabledException("Voting System not yet enabled at height " + Nxt.getBlockchain().getLastBlock().getHeight()); + } + Attachment.MessagingPollCreation attachment = (Attachment.MessagingPollCreation) transaction.getAttachment(); + for (int i = 0; i < attachment.getPollOptions().length; i++) { + if (attachment.getPollOptions()[i].length() > Constants.MAX_POLL_OPTION_LENGTH) { + throw new NxtException.NotValidException("Invalid poll options length: " + attachment.getJSONObject()); + } + } + if (attachment.getPollName().length() > Constants.MAX_POLL_NAME_LENGTH + || attachment.getPollDescription().length() > Constants.MAX_POLL_DESCRIPTION_LENGTH + || attachment.getPollOptions().length > Constants.MAX_POLL_OPTION_COUNT) { + throw new NxtException.NotValidException("Invalid poll attachment: " + attachment.getJSONObject()); + } + } + + @Override + public boolean hasRecipient() { + return false; + } + + }; + + public final static TransactionType VOTE_CASTING = new Messaging() { + + @Override + public final byte getSubtype() { + return TransactionType.SUBTYPE_MESSAGING_VOTE_CASTING; + } + + @Override + Attachment.MessagingVoteCasting parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { + return new Attachment.MessagingVoteCasting(buffer, transactionVersion); + } + + @Override + Attachment.MessagingVoteCasting parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { + return new Attachment.MessagingVoteCasting(attachmentData); + } + + @Override + void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { + Attachment.MessagingVoteCasting attachment = (Attachment.MessagingVoteCasting) transaction.getAttachment(); + Poll poll = Poll.getPoll(attachment.getPollId()); + if (poll != null) { + Vote.addVote(transaction, attachment); + } + } + + @Override + void validateAttachment(Transaction transaction) throws NxtException.ValidationException { + if (Nxt.getBlockchain().getLastBlock().getHeight() < Constants.VOTING_SYSTEM_BLOCK) { + throw new NxtException.NotYetEnabledException("Voting System not yet enabled at height " + Nxt.getBlockchain().getLastBlock().getHeight()); + } + Attachment.MessagingVoteCasting attachment = (Attachment.MessagingVoteCasting) transaction.getAttachment(); + if (attachment.getPollId() == 0 || attachment.getPollVote() == null + || attachment.getPollVote().length > Constants.MAX_POLL_OPTION_COUNT) { + throw new NxtException.NotValidException("Invalid vote casting attachment: " + attachment.getJSONObject()); + } + if (Poll.getPoll(attachment.getPollId()) == null) { + throw new NxtException.NotCurrentlyValidException("Invalid poll: " + Convert.toUnsignedLong(attachment.getPollId())); + } + } + + @Override + public boolean hasRecipient() { + return false; + } + + }; + + public static final TransactionType HUB_ANNOUNCEMENT = new Messaging() { + + @Override + public final byte getSubtype() { + return TransactionType.SUBTYPE_MESSAGING_HUB_ANNOUNCEMENT; + } + + @Override + Attachment.MessagingHubAnnouncement parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { + return new Attachment.MessagingHubAnnouncement(buffer, transactionVersion); + } + + @Override + Attachment.MessagingHubAnnouncement parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { + return new Attachment.MessagingHubAnnouncement(attachmentData); + } + + @Override + void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { + Attachment.MessagingHubAnnouncement attachment = (Attachment.MessagingHubAnnouncement) transaction.getAttachment(); + Hub.addOrUpdateHub(transaction, attachment); + } + + @Override + void validateAttachment(Transaction transaction) throws NxtException.ValidationException { + if (Nxt.getBlockchain().getLastBlock().getHeight() < Constants.TRANSPARENT_FORGING_BLOCK_7) { + throw new NxtException.NotYetEnabledException("Hub terminal announcement not yet enabled at height " + Nxt.getBlockchain().getLastBlock().getHeight()); + } + Attachment.MessagingHubAnnouncement attachment = (Attachment.MessagingHubAnnouncement) transaction.getAttachment(); + if (attachment.getMinFeePerByteNQT() < 0 || attachment.getMinFeePerByteNQT() > Constants.MAX_BALANCE_NQT + || attachment.getUris().length > Constants.MAX_HUB_ANNOUNCEMENT_URIS) { + // cfb: "0" is allowed to show that another way to determine the min fee should be used + throw new NxtException.NotValidException("Invalid hub terminal announcement: " + attachment.getJSONObject()); + } + for (String uri : attachment.getUris()) { + if (uri.length() > Constants.MAX_HUB_ANNOUNCEMENT_URI_LENGTH) { + throw new NxtException.NotValidException("Invalid URI length: " + uri.length()); + } + //TODO: also check URI validity here? + } + } + + @Override + public boolean hasRecipient() { + return false; + } + + }; + + public static final Messaging ACCOUNT_INFO = new Messaging() { + + @Override + public byte getSubtype() { + return TransactionType.SUBTYPE_MESSAGING_ACCOUNT_INFO; + } + + @Override + Attachment.MessagingAccountInfo parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { + return new Attachment.MessagingAccountInfo(buffer, transactionVersion); + } + + @Override + Attachment.MessagingAccountInfo parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { + return new Attachment.MessagingAccountInfo(attachmentData); + } + + @Override + void validateAttachment(Transaction transaction) throws NxtException.ValidationException { + Attachment.MessagingAccountInfo attachment = (Attachment.MessagingAccountInfo)transaction.getAttachment(); + if (attachment.getName().length() > Constants.MAX_ACCOUNT_NAME_LENGTH + || attachment.getDescription().length() > Constants.MAX_ACCOUNT_DESCRIPTION_LENGTH + ) { + throw new NxtException.NotValidException("Invalid account info issuance: " + attachment.getJSONObject()); + } + } + + @Override + void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { + Attachment.MessagingAccountInfo attachment = (Attachment.MessagingAccountInfo) transaction.getAttachment(); + senderAccount.setAccountInfo(attachment.getName(), attachment.getDescription()); + } + + @Override + public boolean hasRecipient() { + return false; + } + + }; + + } + + public static abstract class ColoredCoins extends TransactionType { + + private ColoredCoins() {} + + @Override + public final byte getType() { + return TransactionType.TYPE_COLORED_COINS; + } + + public static final TransactionType ASSET_ISSUANCE = new ColoredCoins() { + + @Override + public final byte getSubtype() { + return TransactionType.SUBTYPE_COLORED_COINS_ASSET_ISSUANCE; + } + + @Override + public Fee getBaselineFee() { + return BASELINE_ASSET_ISSUANCE_FEE; + } + + @Override + public Fee getNextFee() { + return NEXT_ASSET_ISSUANCE_FEE; + } + + @Override + Attachment.ColoredCoinsAssetIssuance parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { + return new Attachment.ColoredCoinsAssetIssuance(buffer, transactionVersion); + } + + @Override + Attachment.ColoredCoinsAssetIssuance parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { + return new Attachment.ColoredCoinsAssetIssuance(attachmentData); + } + + @Override + boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { + return true; + } + + @Override + void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { + Attachment.ColoredCoinsAssetIssuance attachment = (Attachment.ColoredCoinsAssetIssuance) transaction.getAttachment(); + long assetId = transaction.getId(); + Asset.addAsset(transaction, attachment); + senderAccount.addToAssetAndUnconfirmedAssetBalanceQNT(assetId, attachment.getQuantityQNT()); + } + + @Override + void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { + } + + @Override + void validateAttachment(Transaction transaction) throws NxtException.ValidationException { + Attachment.ColoredCoinsAssetIssuance attachment = (Attachment.ColoredCoinsAssetIssuance)transaction.getAttachment(); + if (attachment.getName().length() < Constants.MIN_ASSET_NAME_LENGTH + || attachment.getName().length() > Constants.MAX_ASSET_NAME_LENGTH + || attachment.getDescription().length() > Constants.MAX_ASSET_DESCRIPTION_LENGTH + || attachment.getDecimals() < 0 || attachment.getDecimals() > 8 + || attachment.getQuantityQNT() <= 0 + || attachment.getQuantityQNT() > Constants.MAX_ASSET_QUANTITY_QNT + ) { + throw new NxtException.NotValidException("Invalid asset issuance: " + attachment.getJSONObject()); + } + String normalizedName = attachment.getName().toLowerCase(); + for (int i = 0; i < normalizedName.length(); i++) { + if (Constants.ALPHABET.indexOf(normalizedName.charAt(i)) < 0) { + throw new NxtException.NotValidException("Invalid asset name: " + normalizedName); + } + } + } + + @Override + public boolean hasRecipient() { + return false; + } + + }; + + public static final TransactionType ASSET_TRANSFER = new ColoredCoins() { + + @Override + public final byte getSubtype() { + return TransactionType.SUBTYPE_COLORED_COINS_ASSET_TRANSFER; + } + + @Override + Attachment.ColoredCoinsAssetTransfer parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { + return new Attachment.ColoredCoinsAssetTransfer(buffer, transactionVersion); + } + + @Override + Attachment.ColoredCoinsAssetTransfer parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { + return new Attachment.ColoredCoinsAssetTransfer(attachmentData); + } + + @Override + boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { + Attachment.ColoredCoinsAssetTransfer attachment = (Attachment.ColoredCoinsAssetTransfer) transaction.getAttachment(); + long unconfirmedAssetBalance = senderAccount.getUnconfirmedAssetBalanceQNT(attachment.getAssetId()); + if (unconfirmedAssetBalance >= 0 && unconfirmedAssetBalance >= attachment.getQuantityQNT()) { + senderAccount.addToUnconfirmedAssetBalanceQNT(attachment.getAssetId(), -attachment.getQuantityQNT()); + return true; + } + return false; + } + + @Override + void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { + Attachment.ColoredCoinsAssetTransfer attachment = (Attachment.ColoredCoinsAssetTransfer) transaction.getAttachment(); + senderAccount.addToAssetBalanceQNT(attachment.getAssetId(), -attachment.getQuantityQNT()); + recipientAccount.addToAssetAndUnconfirmedAssetBalanceQNT(attachment.getAssetId(), attachment.getQuantityQNT()); + AssetTransfer.addAssetTransfer(transaction, attachment); + } + + @Override + void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { + Attachment.ColoredCoinsAssetTransfer attachment = (Attachment.ColoredCoinsAssetTransfer) transaction.getAttachment(); + senderAccount.addToUnconfirmedAssetBalanceQNT(attachment.getAssetId(), attachment.getQuantityQNT()); + } + + @Override + void validateAttachment(Transaction transaction) throws NxtException.ValidationException { + Attachment.ColoredCoinsAssetTransfer attachment = (Attachment.ColoredCoinsAssetTransfer)transaction.getAttachment(); + if (transaction.getAmountNQT() != 0 + || attachment.getComment() != null && attachment.getComment().length() > Constants.MAX_ASSET_TRANSFER_COMMENT_LENGTH + || attachment.getAssetId() == 0) { + throw new NxtException.NotValidException("Invalid asset transfer amount or comment: " + attachment.getJSONObject()); + } + if (transaction.getVersion() > 0 && attachment.getComment() != null) { + throw new NxtException.NotValidException("Asset transfer comments no longer allowed, use message " + + "or encrypted message appendix instead"); + } + Asset asset = Asset.getAsset(attachment.getAssetId()); + if (attachment.getQuantityQNT() <= 0 || (asset != null && attachment.getQuantityQNT() > asset.getQuantityQNT())) { + throw new NxtException.NotValidException("Invalid asset transfer asset or quantity: " + attachment.getJSONObject()); + } + if (asset == null) { + throw new NxtException.NotCurrentlyValidException("Asset " + Convert.toUnsignedLong(attachment.getAssetId()) + + " does not exist yet"); + } + } + + @Override + public boolean hasRecipient() { + return true; + } + + }; + + abstract static class ColoredCoinsOrderPlacement extends ColoredCoins { + + @Override + final void validateAttachment(Transaction transaction) throws NxtException.ValidationException { + Attachment.ColoredCoinsOrderPlacement attachment = (Attachment.ColoredCoinsOrderPlacement)transaction.getAttachment(); + if (attachment.getPriceNQT() <= 0 || attachment.getPriceNQT() > Constants.MAX_BALANCE_NQT + || attachment.getAssetId() == 0) { + throw new NxtException.NotValidException("Invalid asset order placement: " + attachment.getJSONObject()); + } + Asset asset = Asset.getAsset(attachment.getAssetId()); + if (attachment.getQuantityQNT() <= 0 || (asset != null && attachment.getQuantityQNT() > asset.getQuantityQNT())) { + throw new NxtException.NotValidException("Invalid asset order placement asset or quantity: " + attachment.getJSONObject()); + } + if (asset == null) { + throw new NxtException.NotCurrentlyValidException("Asset " + Convert.toUnsignedLong(attachment.getAssetId()) + + " does not exist yet"); + } + } + + @Override + final public boolean hasRecipient() { + return false; + } + + } + + public static final TransactionType ASK_ORDER_PLACEMENT = new ColoredCoins.ColoredCoinsOrderPlacement() { + + @Override + public final byte getSubtype() { + return TransactionType.SUBTYPE_COLORED_COINS_ASK_ORDER_PLACEMENT; + } + + @Override + Attachment.ColoredCoinsAskOrderPlacement parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { + return new Attachment.ColoredCoinsAskOrderPlacement(buffer, transactionVersion); + } + + @Override + Attachment.ColoredCoinsAskOrderPlacement parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { + return new Attachment.ColoredCoinsAskOrderPlacement(attachmentData); + } + + @Override + boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { + Attachment.ColoredCoinsAskOrderPlacement attachment = (Attachment.ColoredCoinsAskOrderPlacement) transaction.getAttachment(); + long unconfirmedAssetBalance = senderAccount.getUnconfirmedAssetBalanceQNT(attachment.getAssetId()); + if (unconfirmedAssetBalance >= 0 && unconfirmedAssetBalance >= attachment.getQuantityQNT()) { + senderAccount.addToUnconfirmedAssetBalanceQNT(attachment.getAssetId(), -attachment.getQuantityQNT()); + return true; + } + return false; + } + + @Override + void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { + Attachment.ColoredCoinsAskOrderPlacement attachment = (Attachment.ColoredCoinsAskOrderPlacement) transaction.getAttachment(); + if (Asset.getAsset(attachment.getAssetId()) != null) { + Order.Ask.addOrder(transaction, attachment); + } + } + + @Override + void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { + Attachment.ColoredCoinsAskOrderPlacement attachment = (Attachment.ColoredCoinsAskOrderPlacement) transaction.getAttachment(); + senderAccount.addToUnconfirmedAssetBalanceQNT(attachment.getAssetId(), attachment.getQuantityQNT()); + } + + }; + + public final static TransactionType BID_ORDER_PLACEMENT = new ColoredCoins.ColoredCoinsOrderPlacement() { + + @Override + public final byte getSubtype() { + return TransactionType.SUBTYPE_COLORED_COINS_BID_ORDER_PLACEMENT; + } + + @Override + Attachment.ColoredCoinsBidOrderPlacement parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { + return new Attachment.ColoredCoinsBidOrderPlacement(buffer, transactionVersion); + } + + @Override + Attachment.ColoredCoinsBidOrderPlacement parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { + return new Attachment.ColoredCoinsBidOrderPlacement(attachmentData); + } + + @Override + boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { + Attachment.ColoredCoinsBidOrderPlacement attachment = (Attachment.ColoredCoinsBidOrderPlacement) transaction.getAttachment(); + if (senderAccount.getUnconfirmedBalanceNQT() >= Convert.safeMultiply(attachment.getQuantityQNT(), attachment.getPriceNQT())) { + senderAccount.addToUnconfirmedBalanceNQT(-Convert.safeMultiply(attachment.getQuantityQNT(), attachment.getPriceNQT())); + return true; + } + return false; + } + + @Override + void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { + Attachment.ColoredCoinsBidOrderPlacement attachment = (Attachment.ColoredCoinsBidOrderPlacement) transaction.getAttachment(); + if (Asset.getAsset(attachment.getAssetId()) != null) { + Order.Bid.addOrder(transaction, attachment); + } + } + + @Override + void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { + Attachment.ColoredCoinsBidOrderPlacement attachment = (Attachment.ColoredCoinsBidOrderPlacement) transaction.getAttachment(); + senderAccount.addToUnconfirmedBalanceNQT(Convert.safeMultiply(attachment.getQuantityQNT(), attachment.getPriceNQT())); + } + + }; + + abstract static class ColoredCoinsOrderCancellation extends ColoredCoins { + + @Override + final boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { + return true; + } + + @Override + final void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { + } + + @Override + public boolean hasRecipient() { + return false; + } + + } + + public static final TransactionType ASK_ORDER_CANCELLATION = new ColoredCoins.ColoredCoinsOrderCancellation() { + + @Override + public final byte getSubtype() { + return TransactionType.SUBTYPE_COLORED_COINS_ASK_ORDER_CANCELLATION; + } + + @Override + Attachment.ColoredCoinsAskOrderCancellation parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { + return new Attachment.ColoredCoinsAskOrderCancellation(buffer, transactionVersion); + } + + @Override + Attachment.ColoredCoinsAskOrderCancellation parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { + return new Attachment.ColoredCoinsAskOrderCancellation(attachmentData); + } + + @Override + void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { + Attachment.ColoredCoinsAskOrderCancellation attachment = (Attachment.ColoredCoinsAskOrderCancellation) transaction.getAttachment(); + Order order = Order.Ask.getAskOrder(attachment.getOrderId()); + Order.Ask.removeOrder(attachment.getOrderId()); + if (order != null) { + senderAccount.addToUnconfirmedAssetBalanceQNT(order.getAssetId(), order.getQuantityQNT()); + } + } + + @Override + void validateAttachment(Transaction transaction) throws NxtException.ValidationException { + Attachment.ColoredCoinsAskOrderCancellation attachment = (Attachment.ColoredCoinsAskOrderCancellation) transaction.getAttachment(); + Order ask = Order.Ask.getAskOrder(attachment.getOrderId()); + if(ask == null) { + throw new NxtException.NotCurrentlyValidException("Invalid ask order: " + Convert.toUnsignedLong(attachment.getOrderId())); + } + if(ask.getAccountId() != transaction.getSenderId()) { + throw new NxtException.NotValidException("Order " + Convert.toUnsignedLong(attachment.getOrderId()) + " was created by account " + + Convert.toUnsignedLong(ask.getAccountId())); + } + } + + }; + + public static final TransactionType BID_ORDER_CANCELLATION = new ColoredCoins.ColoredCoinsOrderCancellation() { + + @Override + public final byte getSubtype() { + return TransactionType.SUBTYPE_COLORED_COINS_BID_ORDER_CANCELLATION; + } + + @Override + Attachment.ColoredCoinsBidOrderCancellation parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { + return new Attachment.ColoredCoinsBidOrderCancellation(buffer, transactionVersion); + } + + @Override + Attachment.ColoredCoinsBidOrderCancellation parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { + return new Attachment.ColoredCoinsBidOrderCancellation(attachmentData); + } + + @Override + void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { + Attachment.ColoredCoinsBidOrderCancellation attachment = (Attachment.ColoredCoinsBidOrderCancellation) transaction.getAttachment(); + Order order = Order.Bid.getBidOrder(attachment.getOrderId()); + Order.Bid.removeOrder(attachment.getOrderId()); + if (order != null) { + senderAccount.addToUnconfirmedBalanceNQT(Convert.safeMultiply(order.getQuantityQNT(), order.getPriceNQT())); + } + } + + @Override + void validateAttachment(Transaction transaction) throws NxtException.ValidationException { + Attachment.ColoredCoinsBidOrderCancellation attachment = (Attachment.ColoredCoinsBidOrderCancellation) transaction.getAttachment(); + Order bid = Order.Bid.getBidOrder(attachment.getOrderId()); + if(bid == null) { + throw new NxtException.NotCurrentlyValidException("Invalid bid order: " + Convert.toUnsignedLong(attachment.getOrderId())); + } + if(bid.getAccountId() != transaction.getSenderId()) { + throw new NxtException.NotValidException("Order " + Convert.toUnsignedLong(attachment.getOrderId()) + " was created by account " + + Convert.toUnsignedLong(bid.getAccountId())); + } + } + + }; + } + + public static abstract class DigitalGoods extends TransactionType { + + private DigitalGoods() { + } + + @Override + public final byte getType() { + return TransactionType.TYPE_DIGITAL_GOODS; + } + + @Override + boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { + return true; + } + + @Override + void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { + } + + @Override + final void validateAttachment(Transaction transaction) throws NxtException.ValidationException { + if (Nxt.getBlockchain().getLastBlock().getHeight() < Constants.DIGITAL_GOODS_STORE_BLOCK) { + throw new NxtException.NotYetEnabledException("Digital goods listing not yet enabled at height " + Nxt.getBlockchain().getLastBlock().getHeight()); + } + if (transaction.getAmountNQT() != 0) { + throw new NxtException.NotValidException("Invalid digital goods transaction"); + } + doValidateAttachment(transaction); + } + + abstract void doValidateAttachment(Transaction transaction) throws NxtException.ValidationException; + + + public static final TransactionType LISTING = new DigitalGoods() { + + @Override + public final byte getSubtype() { + return TransactionType.SUBTYPE_DIGITAL_GOODS_LISTING; + } + + @Override + Attachment.DigitalGoodsListing parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { + return new Attachment.DigitalGoodsListing(buffer, transactionVersion); + } + + @Override + Attachment.DigitalGoodsListing parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { + return new Attachment.DigitalGoodsListing(attachmentData); + } + + @Override + void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { + Attachment.DigitalGoodsListing attachment = (Attachment.DigitalGoodsListing) transaction.getAttachment(); + DigitalGoodsStore.listGoods(transaction, attachment); + } + + @Override + void doValidateAttachment(Transaction transaction) throws NxtException.ValidationException { + Attachment.DigitalGoodsListing attachment = (Attachment.DigitalGoodsListing) transaction.getAttachment(); + if (attachment.getName().length() == 0 + || attachment.getName().length() > Constants.MAX_DGS_LISTING_NAME_LENGTH + || attachment.getDescription().length() > Constants.MAX_DGS_LISTING_DESCRIPTION_LENGTH + || attachment.getTags().length() > Constants.MAX_DGS_LISTING_TAGS_LENGTH + || attachment.getQuantity() < 0 || attachment.getQuantity() > Constants.MAX_DGS_LISTING_QUANTITY + || attachment.getPriceNQT() <= 0 || attachment.getPriceNQT() > Constants.MAX_BALANCE_NQT) { + throw new NxtException.NotValidException("Invalid digital goods listing: " + attachment.getJSONObject()); + } + } + + @Override + public boolean hasRecipient() { + return false; + } + + }; + + public static final TransactionType DELISTING = new DigitalGoods() { + + @Override + public final byte getSubtype() { + return TransactionType.SUBTYPE_DIGITAL_GOODS_DELISTING; + } + + @Override + Attachment.DigitalGoodsDelisting parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { + return new Attachment.DigitalGoodsDelisting(buffer, transactionVersion); + } + + @Override + Attachment.DigitalGoodsDelisting parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { + return new Attachment.DigitalGoodsDelisting(attachmentData); + } + + @Override + void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { + Attachment.DigitalGoodsDelisting attachment = (Attachment.DigitalGoodsDelisting) transaction.getAttachment(); + DigitalGoodsStore.delistGoods(attachment.getGoodsId()); + } + + @Override + void doValidateAttachment(Transaction transaction) throws NxtException.ValidationException { + Attachment.DigitalGoodsDelisting attachment = (Attachment.DigitalGoodsDelisting) transaction.getAttachment(); + DigitalGoodsStore.Goods goods = DigitalGoodsStore.getGoods(attachment.getGoodsId()); + if (goods != null && transaction.getSenderId() != goods.getSellerId()) { + throw new NxtException.NotValidException("Invalid digital goods delisting - seller is different: " + attachment.getJSONObject()); + } + if (goods == null || goods.isDelisted()) { + throw new NxtException.NotCurrentlyValidException("Goods " + Convert.toUnsignedLong(attachment.getGoodsId()) + + "not yet listed or already delisted"); + } + } + + @Override + boolean isDuplicate(Transaction transaction, Map> duplicates) { + Attachment.DigitalGoodsDelisting attachment = (Attachment.DigitalGoodsDelisting) transaction.getAttachment(); + return isDuplicate(DigitalGoods.DELISTING, Convert.toUnsignedLong(attachment.getGoodsId()), duplicates); + } + + @Override + public boolean hasRecipient() { + return false; + } + + }; + + public static final TransactionType PRICE_CHANGE = new DigitalGoods() { + + @Override + public final byte getSubtype() { + return TransactionType.SUBTYPE_DIGITAL_GOODS_PRICE_CHANGE; + } + + @Override + Attachment.DigitalGoodsPriceChange parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { + return new Attachment.DigitalGoodsPriceChange(buffer, transactionVersion); + } + + @Override + Attachment.DigitalGoodsPriceChange parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { + return new Attachment.DigitalGoodsPriceChange(attachmentData); + } + + @Override + void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { + Attachment.DigitalGoodsPriceChange attachment = (Attachment.DigitalGoodsPriceChange) transaction.getAttachment(); + DigitalGoodsStore.changePrice(attachment.getGoodsId(), attachment.getPriceNQT()); + } + + @Override + void doValidateAttachment(Transaction transaction) throws NxtException.ValidationException { + Attachment.DigitalGoodsPriceChange attachment = (Attachment.DigitalGoodsPriceChange) transaction.getAttachment(); + DigitalGoodsStore.Goods goods = DigitalGoodsStore.getGoods(attachment.getGoodsId()); + if (attachment.getPriceNQT() <= 0 || attachment.getPriceNQT() > Constants.MAX_BALANCE_NQT + || (goods != null && transaction.getSenderId() != goods.getSellerId())) { + throw new NxtException.NotValidException("Invalid digital goods price change: " + attachment.getJSONObject()); + } + if (goods == null || goods.isDelisted()) { + throw new NxtException.NotCurrentlyValidException("Goods " + Convert.toUnsignedLong(attachment.getGoodsId()) + + "not yet listed or already delisted"); + } + } + + @Override + boolean isDuplicate(Transaction transaction, Map> duplicates) { + Attachment.DigitalGoodsPriceChange attachment = (Attachment.DigitalGoodsPriceChange) transaction.getAttachment(); + // not a bug, uniqueness is based on DigitalGoods.DELISTING + return isDuplicate(DigitalGoods.DELISTING, Convert.toUnsignedLong(attachment.getGoodsId()), duplicates); + } + + @Override + public boolean hasRecipient() { + return false; + } + + }; + + public static final TransactionType QUANTITY_CHANGE = new DigitalGoods() { + + @Override + public final byte getSubtype() { + return TransactionType.SUBTYPE_DIGITAL_GOODS_QUANTITY_CHANGE; + } + + @Override + Attachment.DigitalGoodsQuantityChange parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { + return new Attachment.DigitalGoodsQuantityChange(buffer, transactionVersion); + } + + @Override + Attachment.DigitalGoodsQuantityChange parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { + return new Attachment.DigitalGoodsQuantityChange(attachmentData); + } + + @Override + void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { + Attachment.DigitalGoodsQuantityChange attachment = (Attachment.DigitalGoodsQuantityChange) transaction.getAttachment(); + DigitalGoodsStore.changeQuantity(attachment.getGoodsId(), attachment.getDeltaQuantity()); + } + + @Override + void doValidateAttachment(Transaction transaction) throws NxtException.ValidationException { + Attachment.DigitalGoodsQuantityChange attachment = (Attachment.DigitalGoodsQuantityChange) transaction.getAttachment(); + DigitalGoodsStore.Goods goods = DigitalGoodsStore.getGoods(attachment.getGoodsId()); + if (attachment.getDeltaQuantity() < -Constants.MAX_DGS_LISTING_QUANTITY + || attachment.getDeltaQuantity() > Constants.MAX_DGS_LISTING_QUANTITY + || (goods != null && transaction.getSenderId() != goods.getSellerId())) { + throw new NxtException.NotValidException("Invalid digital goods quantity change: " + attachment.getJSONObject()); + } + if (goods == null || goods.isDelisted()) { + throw new NxtException.NotCurrentlyValidException("Goods " + Convert.toUnsignedLong(attachment.getGoodsId()) + + "not yet listed or already delisted"); + } + } + + @Override + boolean isDuplicate(Transaction transaction, Map> duplicates) { + Attachment.DigitalGoodsQuantityChange attachment = (Attachment.DigitalGoodsQuantityChange) transaction.getAttachment(); + // not a bug, uniqueness is based on DigitalGoods.DELISTING + return isDuplicate(DigitalGoods.DELISTING, Convert.toUnsignedLong(attachment.getGoodsId()), duplicates); + } + + @Override + public boolean hasRecipient() { + return false; + } + + }; + + public static final TransactionType PURCHASE = new DigitalGoods() { + + @Override + public final byte getSubtype() { + return TransactionType.SUBTYPE_DIGITAL_GOODS_PURCHASE; + } + + @Override + Attachment.DigitalGoodsPurchase parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { + return new Attachment.DigitalGoodsPurchase(buffer, transactionVersion); + } + + @Override + Attachment.DigitalGoodsPurchase parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { + return new Attachment.DigitalGoodsPurchase(attachmentData); + } + + @Override + boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { + Attachment.DigitalGoodsPurchase attachment = (Attachment.DigitalGoodsPurchase) transaction.getAttachment(); + if (senderAccount.getUnconfirmedBalanceNQT() >= Convert.safeMultiply(attachment.getQuantity(), attachment.getPriceNQT())) { + senderAccount.addToUnconfirmedBalanceNQT(-Convert.safeMultiply(attachment.getQuantity(), attachment.getPriceNQT())); + return true; + } + return false; + } + + @Override + void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { + Attachment.DigitalGoodsPurchase attachment = (Attachment.DigitalGoodsPurchase) transaction.getAttachment(); + senderAccount.addToUnconfirmedBalanceNQT(Convert.safeMultiply(attachment.getQuantity(), attachment.getPriceNQT())); + } + + @Override + void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { + Attachment.DigitalGoodsPurchase attachment = (Attachment.DigitalGoodsPurchase) transaction.getAttachment(); + DigitalGoodsStore.purchase(transaction, attachment); + } + + @Override + void doValidateAttachment(Transaction transaction) throws NxtException.ValidationException { + Attachment.DigitalGoodsPurchase attachment = (Attachment.DigitalGoodsPurchase) transaction.getAttachment(); + DigitalGoodsStore.Goods goods = DigitalGoodsStore.getGoods(attachment.getGoodsId()); + if (attachment.getQuantity() <= 0 || attachment.getQuantity() > Constants.MAX_DGS_LISTING_QUANTITY + || attachment.getPriceNQT() <= 0 || attachment.getPriceNQT() > Constants.MAX_BALANCE_NQT + || (goods != null && goods.getSellerId() != transaction.getRecipientId())) { + throw new NxtException.NotValidException("Invalid digital goods purchase: " + attachment.getJSONObject()); + } + if (transaction.getEncryptedMessage() != null && ! transaction.getEncryptedMessage().isText()) { + throw new NxtException.NotValidException("Only text encrypted messages allowed"); + } + if (goods == null || goods.isDelisted()) { + throw new NxtException.NotCurrentlyValidException("Goods " + Convert.toUnsignedLong(attachment.getGoodsId()) + + "not yet listed or already delisted"); + } + if (attachment.getQuantity() > goods.getQuantity() || attachment.getPriceNQT() != goods.getPriceNQT()) { + throw new NxtException.NotCurrentlyValidException("Goods price or quantity changed: " + attachment.getJSONObject()); + } + if (attachment.getDeliveryDeadlineTimestamp() <= Nxt.getBlockchain().getLastBlock().getTimestamp()) { + throw new NxtException.NotCurrentlyValidException("Delivery deadline has already expired: " + attachment.getDeliveryDeadlineTimestamp()); + } + } + + @Override + public boolean hasRecipient() { + return true; + } + + }; + + public static final TransactionType DELIVERY = new DigitalGoods() { + + @Override + public final byte getSubtype() { + return TransactionType.SUBTYPE_DIGITAL_GOODS_DELIVERY; + } + + @Override + Attachment.DigitalGoodsDelivery parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { + return new Attachment.DigitalGoodsDelivery(buffer, transactionVersion); + } + + @Override + Attachment.DigitalGoodsDelivery parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { + return new Attachment.DigitalGoodsDelivery(attachmentData); + } + + @Override + void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { + Attachment.DigitalGoodsDelivery attachment = (Attachment.DigitalGoodsDelivery)transaction.getAttachment(); + DigitalGoodsStore.deliver(transaction, attachment); + } + + @Override + void doValidateAttachment(Transaction transaction) throws NxtException.ValidationException { + Attachment.DigitalGoodsDelivery attachment = (Attachment.DigitalGoodsDelivery) transaction.getAttachment(); + DigitalGoodsStore.Purchase purchase = DigitalGoodsStore.getPendingPurchase(attachment.getPurchaseId()); + if (attachment.getGoods().getData().length > Constants.MAX_DGS_GOODS_LENGTH + || attachment.getGoods().getData().length == 0 + || attachment.getGoods().getNonce().length != 32 + || attachment.getDiscountNQT() < 0 || attachment.getDiscountNQT() > Constants.MAX_BALANCE_NQT + || (purchase != null && + (purchase.getBuyerId() != transaction.getRecipientId() + || transaction.getSenderId() != purchase.getSellerId() + || attachment.getDiscountNQT() > Convert.safeMultiply(purchase.getPriceNQT(), purchase.getQuantity())))) { + throw new NxtException.NotValidException("Invalid digital goods delivery: " + attachment.getJSONObject()); + } + if (purchase == null || purchase.getEncryptedGoods() != null) { + throw new NxtException.NotCurrentlyValidException("Purchase does not exist yet, or already delivered: " + + attachment.getJSONObject()); + } + } + + @Override + boolean isDuplicate(Transaction transaction, Map> duplicates) { + Attachment.DigitalGoodsDelivery attachment = (Attachment.DigitalGoodsDelivery) transaction.getAttachment(); + return isDuplicate(DigitalGoods.DELIVERY, Convert.toUnsignedLong(attachment.getPurchaseId()), duplicates); + } + + @Override + public boolean hasRecipient() { + return true; + } + + }; + + public static final TransactionType FEEDBACK = new DigitalGoods() { + + @Override + public final byte getSubtype() { + return TransactionType.SUBTYPE_DIGITAL_GOODS_FEEDBACK; + } + + @Override + Attachment.DigitalGoodsFeedback parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { + return new Attachment.DigitalGoodsFeedback(buffer, transactionVersion); + } + + @Override + Attachment.DigitalGoodsFeedback parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { + return new Attachment.DigitalGoodsFeedback(attachmentData); + } + + @Override + void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { + Attachment.DigitalGoodsFeedback attachment = (Attachment.DigitalGoodsFeedback)transaction.getAttachment(); + DigitalGoodsStore.feedback(attachment.getPurchaseId(), transaction.getEncryptedMessage(), transaction.getMessage()); + } + + @Override + void doValidateAttachment(Transaction transaction) throws NxtException.ValidationException { + Attachment.DigitalGoodsFeedback attachment = (Attachment.DigitalGoodsFeedback) transaction.getAttachment(); + DigitalGoodsStore.Purchase purchase = DigitalGoodsStore.getPurchase(attachment.getPurchaseId()); + if (purchase != null && + (purchase.getSellerId() != transaction.getRecipientId() + || transaction.getSenderId() != purchase.getBuyerId())) { + throw new NxtException.NotValidException("Invalid digital goods feedback: " + attachment.getJSONObject()); + } + if (transaction.getEncryptedMessage() == null && transaction.getMessage() == null) { + throw new NxtException.NotValidException("Missing feedback message"); + } + if (transaction.getEncryptedMessage() != null && ! transaction.getEncryptedMessage().isText()) { + throw new NxtException.NotValidException("Only text encrypted messages allowed"); + } + if (transaction.getMessage() != null && ! transaction.getMessage().isText()) { + throw new NxtException.NotValidException("Only text public messages allowed"); + } + if (purchase == null || purchase.getEncryptedGoods() == null) { + throw new NxtException.NotCurrentlyValidException("Purchase does not exist yet or not yet delivered"); + } + } + + @Override + boolean isDuplicate(Transaction transaction, Map> duplicates) { + Attachment.DigitalGoodsFeedback attachment = (Attachment.DigitalGoodsFeedback) transaction.getAttachment(); + return isDuplicate(DigitalGoods.FEEDBACK, Convert.toUnsignedLong(attachment.getPurchaseId()), duplicates); + } + + @Override + public boolean hasRecipient() { + return true; + } + + }; + + public static final TransactionType REFUND = new DigitalGoods() { + + @Override + public final byte getSubtype() { + return TransactionType.SUBTYPE_DIGITAL_GOODS_REFUND; + } + + @Override + Attachment.DigitalGoodsRefund parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { + return new Attachment.DigitalGoodsRefund(buffer, transactionVersion); + } + + @Override + Attachment.DigitalGoodsRefund parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { + return new Attachment.DigitalGoodsRefund(attachmentData); + } + + @Override + boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { + Attachment.DigitalGoodsRefund attachment = (Attachment.DigitalGoodsRefund) transaction.getAttachment(); + if (senderAccount.getUnconfirmedBalanceNQT() >= attachment.getRefundNQT()) { + senderAccount.addToUnconfirmedBalanceNQT(-attachment.getRefundNQT()); + return true; + } + return false; + } + + @Override + void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { + Attachment.DigitalGoodsRefund attachment = (Attachment.DigitalGoodsRefund) transaction.getAttachment(); + senderAccount.addToUnconfirmedBalanceNQT(attachment.getRefundNQT()); + } + + @Override + void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { + Attachment.DigitalGoodsRefund attachment = (Attachment.DigitalGoodsRefund) transaction.getAttachment(); + DigitalGoodsStore.refund(transaction.getSenderId(), attachment.getPurchaseId(), + attachment.getRefundNQT(), transaction.getEncryptedMessage()); + } + + @Override + void doValidateAttachment(Transaction transaction) throws NxtException.ValidationException { + Attachment.DigitalGoodsRefund attachment = (Attachment.DigitalGoodsRefund) transaction.getAttachment(); + DigitalGoodsStore.Purchase purchase = DigitalGoodsStore.getPurchase(attachment.getPurchaseId()); + if (attachment.getRefundNQT() < 0 || attachment.getRefundNQT() > Constants.MAX_BALANCE_NQT + || (purchase != null && + (purchase.getBuyerId() != transaction.getRecipientId() + || transaction.getSenderId() != purchase.getSellerId()))) { + throw new NxtException.NotValidException("Invalid digital goods refund: " + attachment.getJSONObject()); + } + if (transaction.getEncryptedMessage() != null && ! transaction.getEncryptedMessage().isText()) { + throw new NxtException.NotValidException("Only text encrypted messages allowed"); + } + if (purchase == null || purchase.getEncryptedGoods() == null || purchase.getRefundNQT() != 0) { + throw new NxtException.NotCurrentlyValidException("Purchase does not exist or is not delivered or is already refunded"); + } + } + + @Override + boolean isDuplicate(Transaction transaction, Map> duplicates) { + Attachment.DigitalGoodsRefund attachment = (Attachment.DigitalGoodsRefund) transaction.getAttachment(); + return isDuplicate(DigitalGoods.REFUND, Convert.toUnsignedLong(attachment.getPurchaseId()), duplicates); + } + + @Override + public boolean hasRecipient() { + return true; + } + + }; + + } + + public static abstract class AccountControl extends TransactionType { + + private AccountControl() { + } + + @Override + public final byte getType() { + return TransactionType.TYPE_ACCOUNT_CONTROL; + } + + @Override + final boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { + return true; + } + + @Override + final void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { + } + + public static final TransactionType EFFECTIVE_BALANCE_LEASING = new AccountControl() { + + @Override + public final byte getSubtype() { + return TransactionType.SUBTYPE_ACCOUNT_CONTROL_EFFECTIVE_BALANCE_LEASING; + } + + @Override + Attachment.AccountControlEffectiveBalanceLeasing parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { + return new Attachment.AccountControlEffectiveBalanceLeasing(buffer, transactionVersion); + } + + @Override + Attachment.AccountControlEffectiveBalanceLeasing parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { + return new Attachment.AccountControlEffectiveBalanceLeasing(attachmentData); + } + + @Override + void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { + Attachment.AccountControlEffectiveBalanceLeasing attachment = (Attachment.AccountControlEffectiveBalanceLeasing) transaction.getAttachment(); + Account.getAccount(transaction.getSenderId()).leaseEffectiveBalance(transaction.getRecipientId(), attachment.getPeriod()); + } + + @Override + void validateAttachment(Transaction transaction) throws NxtException.ValidationException { + Attachment.AccountControlEffectiveBalanceLeasing attachment = (Attachment.AccountControlEffectiveBalanceLeasing)transaction.getAttachment(); + Account recipientAccount = Account.getAccount(transaction.getRecipientId()); + if (transaction.getSenderId() == transaction.getRecipientId() + || transaction.getAmountNQT() != 0 + || attachment.getPeriod() < 1440) { + throw new NxtException.NotValidException("Invalid effective balance leasing: " + + transaction.getJSONObject() + " transaction " + transaction.getStringId()); + } + if (recipientAccount == null + || (recipientAccount.getPublicKey() == null && ! transaction.getStringId().equals("5081403377391821646"))) { + throw new NxtException.NotCurrentlyValidException("Invalid effective balance leasing: " + + " recipient account " + transaction.getRecipientId() + " not found or no public key published"); + } + } + + @Override + public boolean hasRecipient() { + return true; + } + + }; + + } + + public static abstract class BurstMining extends TransactionType { + + private BurstMining() {} + + @Override + public final byte getType() { + return TransactionType.TYPE_BURST_MINING; + } + + @Override + final boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { + return true; + } + + @Override + final void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) {} + + public static final TransactionType REWARD_RECIPIENT_ASSIGNMENT = new BurstMining() { + + @Override + public final byte getSubtype() { + return TransactionType.SUBTYPE_BURST_MINING_REWARD_RECIPIENT_ASSIGNMENT; + } + + @Override + Attachment.BurstMiningRewardRecipientAssignment parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { + return new Attachment.BurstMiningRewardRecipientAssignment(buffer, transactionVersion); + } + + @Override + Attachment.BurstMiningRewardRecipientAssignment parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { + return new Attachment.BurstMiningRewardRecipientAssignment(attachmentData); + } + + @Override + void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { + senderAccount.setRewardRecipientAssignment(recipientAccount.getId()); + } + + @Override + boolean isDuplicate(Transaction transaction, Map> duplicates) { + if(Nxt.getBlockchain().getHeight() < Constants.DIGITAL_GOODS_STORE_BLOCK) { + return false; // sync fails after 7007 without this + } + return isDuplicate(BurstMining.REWARD_RECIPIENT_ASSIGNMENT, Convert.toUnsignedLong(transaction.getSenderId()), duplicates); + } + + @Override + void validateAttachment(Transaction transaction) throws NxtException.ValidationException { + long height = Nxt.getBlockchain().getLastBlock().getHeight() + 1; + Account sender = Account.getAccount(transaction.getSenderId()); + Account.RewardRecipientAssignment rewardAssignment = sender.getRewardRecipientAssignment(); + if(rewardAssignment != null && rewardAssignment.getFromHeight() >= height) { + throw new NxtException.NotValidException("Cannot reassign reward recipient before previous goes into effect: " + transaction.getJSONObject()); + } + Account recip = Account.getAccount(transaction.getRecipientId()); + if(recip == null || recip.getPublicKey() == null) { + throw new NxtException.NotValidException("Reward recipient must have public key saved in blockchain: " + transaction.getJSONObject()); + } + if(transaction.getAmountNQT() != 0 || transaction.getFeeNQT() != Constants.ONE_NXT) { + throw new NxtException.NotValidException("Reward recipient assisnment transaction must have 0 send amount and 1 fee: " + transaction.getJSONObject()); + } + if(height < Constants.BURST_REWARD_RECIPIENT_ASSIGNMENT_START_BLOCK) { + throw new NxtException.NotCurrentlyValidException("Reward recipient assignment not allowed before block " + Constants.BURST_REWARD_RECIPIENT_ASSIGNMENT_START_BLOCK); + } + } + + @Override + public boolean hasRecipient() { + return true; + } + }; + } + + public static abstract class AdvancedPayment extends TransactionType { + + private AdvancedPayment() {} + + @Override + public final byte getType() { + return TransactionType.TYPE_ADVANCED_PAYMENT; + } + + public final static TransactionType ESCROW_CREATION = new AdvancedPayment() { + + @Override + public final byte getSubtype() { + return TransactionType.SUBTYPE_ADVANCED_PAYMENT_ESCROW_CREATION; + } + + @Override + Attachment.AdvancedPaymentEscrowCreation parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { + return new Attachment.AdvancedPaymentEscrowCreation(buffer, transactionVersion); + } + + @Override + Attachment.AdvancedPaymentEscrowCreation parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { + return new Attachment.AdvancedPaymentEscrowCreation(attachmentData); + } + + @Override + final boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { + Attachment.AdvancedPaymentEscrowCreation attachment = (Attachment.AdvancedPaymentEscrowCreation) transaction.getAttachment(); + Long totalAmountNQT = Convert.safeAdd(attachment.getAmountNQT(), attachment.getTotalSigners() * Constants.ONE_NXT); + if(senderAccount.getUnconfirmedBalanceNQT() < totalAmountNQT.longValue()) { + return false; + } + senderAccount.addToUnconfirmedBalanceNQT(-totalAmountNQT); + return true; + } + + @Override + final void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { + Attachment.AdvancedPaymentEscrowCreation attachment = (Attachment.AdvancedPaymentEscrowCreation) transaction.getAttachment(); + Long totalAmountNQT = Convert.safeAdd(attachment.getAmountNQT(), attachment.getTotalSigners() * Constants.ONE_NXT); + senderAccount.addToBalanceNQT(-totalAmountNQT); + Collection signers = attachment.getSigners(); + for(Long signer : signers) { + Account.addOrGetAccount(signer).addToBalanceAndUnconfirmedBalanceNQT(Constants.ONE_NXT); + } + Escrow.addEscrowTransaction(senderAccount, + recipientAccount, + transaction.getId(), + attachment.getAmountNQT(), + attachment.getRequiredSigners(), + attachment.getSigners(), + transaction.getTimestamp() + attachment.getDeadline(), + attachment.getDeadlineAction()); + } + + @Override + final void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { + Attachment.AdvancedPaymentEscrowCreation attachment = (Attachment.AdvancedPaymentEscrowCreation) transaction.getAttachment(); + Long totalAmountNQT = Convert.safeAdd(attachment.getAmountNQT(), attachment.getTotalSigners() * Constants.ONE_NXT); + senderAccount.addToUnconfirmedBalanceNQT(totalAmountNQT); + } + + @Override + boolean isDuplicate(Transaction transaction, Map> duplicates) { + return false; + } + + @Override + void validateAttachment(Transaction transaction) throws NxtException.ValidationException { + Attachment.AdvancedPaymentEscrowCreation attachment = (Attachment.AdvancedPaymentEscrowCreation) transaction.getAttachment(); + Long totalAmountNQT = Convert.safeAdd(attachment.getAmountNQT(), transaction.getFeeNQT()); + if(transaction.getSenderId() == transaction.getRecipientId()) { + throw new NxtException.NotValidException("Escrow must have different sender and recipient"); + } + totalAmountNQT = Convert.safeAdd(totalAmountNQT, attachment.getTotalSigners() * Constants.ONE_NXT); + if(transaction.getAmountNQT() != 0) { + throw new NxtException.NotValidException("Transaction sent amount must be 0 for escrow"); + } + if(totalAmountNQT.compareTo(0L) < 0 || + totalAmountNQT.compareTo(Constants.MAX_BALANCE_NQT) > 0) + { + throw new NxtException.NotValidException("Invalid escrow creation amount"); + } + if(transaction.getFeeNQT() < Constants.ONE_NXT) { + throw new NxtException.NotValidException("Escrow transaction must have a fee at least 1 burst"); + } + if(attachment.getRequiredSigners() < 1 || attachment.getRequiredSigners() > 10) { + throw new NxtException.NotValidException("Escrow required signers much be 1 - 10"); + } + if(attachment.getRequiredSigners() > attachment.getTotalSigners()) { + throw new NxtException.NotValidException("Cannot have more required than signers on escrow"); + } + if(attachment.getTotalSigners() < 1 || attachment.getTotalSigners() > 10) { + throw new NxtException.NotValidException("Escrow transaction requires 1 - 10 signers"); + } + if(attachment.getDeadline() < 1 || attachment.getDeadline() > 7776000) { // max deadline 3 months + throw new NxtException.NotValidException("Escrow deadline must be 1 - 7776000 seconds"); + } + if(attachment.getDeadlineAction() == null || attachment.getDeadlineAction() == Escrow.DecisionType.UNDECIDED) { + throw new NxtException.NotValidException("Invalid deadline action for escrow"); + } + if(attachment.getSigners().contains(transaction.getSenderId()) || + attachment.getSigners().contains(transaction.getRecipientId())) { + throw new NxtException.NotValidException("Escrow sender and recipient cannot be signers"); + } + if(!Escrow.isEnabled()) { + throw new NxtException.NotYetEnabledException("Escrow not yet enabled"); + } + } + + @Override + final public boolean hasRecipient() { + return true; + } + }; + + public final static TransactionType ESCROW_SIGN = new AdvancedPayment() { + + @Override + public final byte getSubtype() { + return TransactionType.SUBTYPE_ADVANCED_PAYMENT_ESCROW_SIGN; + } + + @Override + Attachment.AdvancedPaymentEscrowSign parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { + return new Attachment.AdvancedPaymentEscrowSign(buffer, transactionVersion); + } + + @Override + Attachment.AdvancedPaymentEscrowSign parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { + return new Attachment.AdvancedPaymentEscrowSign(attachmentData); + } + + @Override + final boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { + return true; + } + + @Override + final void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { + Attachment.AdvancedPaymentEscrowSign attachment = (Attachment.AdvancedPaymentEscrowSign) transaction.getAttachment(); + Escrow escrow = Escrow.getEscrowTransaction(attachment.getEscrowId()); + escrow.sign(senderAccount.getId(), attachment.getDecision()); + } + + @Override + final void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { + } + + @Override + boolean isDuplicate(Transaction transaction, Map> duplicates) { + Attachment.AdvancedPaymentEscrowSign attachment = (Attachment.AdvancedPaymentEscrowSign) transaction.getAttachment(); + String uniqueString = Convert.toUnsignedLong(attachment.getEscrowId()) + ":" + + Convert.toUnsignedLong(transaction.getSenderId()); + return isDuplicate(AdvancedPayment.ESCROW_SIGN, uniqueString, duplicates); + } + + @Override + void validateAttachment(Transaction transaction) throws NxtException.ValidationException { + Attachment.AdvancedPaymentEscrowSign attachment = (Attachment.AdvancedPaymentEscrowSign) transaction.getAttachment(); + if(transaction.getAmountNQT() != 0 || transaction.getFeeNQT() != Constants.ONE_NXT) { + throw new NxtException.NotValidException("Escrow signing must have amount 0 and fee of 1"); + } + if(attachment.getEscrowId() == null || attachment.getDecision() == null) { + throw new NxtException.NotValidException("Escrow signing requires escrow id and decision set"); + } + Escrow escrow = Escrow.getEscrowTransaction(attachment.getEscrowId()); + if(escrow == null) { + throw new NxtException.NotValidException("Escrow transaction not found"); + } + if(!escrow.isIdSigner(transaction.getSenderId()) && + !escrow.getSenderId().equals(transaction.getSenderId()) && + !escrow.getRecipientId().equals(transaction.getSenderId())) { + throw new NxtException.NotValidException("Sender is not a participant in specified escrow"); + } + if(escrow.getSenderId().equals(transaction.getSenderId()) && attachment.getDecision() != Escrow.DecisionType.RELEASE) { + throw new NxtException.NotValidException("Escrow sender can only release"); + } + if(escrow.getRecipientId().equals(transaction.getSenderId()) && attachment.getDecision() != Escrow.DecisionType.REFUND) { + throw new NxtException.NotValidException("Escrow recipient can only refund"); + } + if(!Escrow.isEnabled()) { + throw new NxtException.NotYetEnabledException("Escrow not yet enabled"); + } + } + + @Override + final public boolean hasRecipient() { + return false; + } + }; + + public final static TransactionType ESCROW_RESULT = new AdvancedPayment() { + + @Override + public final byte getSubtype() { + return TransactionType.SUBTYPE_ADVANCED_PAYMENT_ESCROW_RESULT; + } + + @Override + Attachment.AdvancedPaymentEscrowResult parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { + return new Attachment.AdvancedPaymentEscrowResult(buffer, transactionVersion); + } + + @Override + Attachment.AdvancedPaymentEscrowResult parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { + return new Attachment.AdvancedPaymentEscrowResult(attachmentData); + } + + @Override + final boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { + return false; + } + + @Override + final void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { + } + + @Override + final void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { + } + + @Override + boolean isDuplicate(Transaction transaction, Map> duplicates) { + return true; + } + + @Override + void validateAttachment(Transaction transaction) throws NxtException.ValidationException { + throw new NxtException.NotValidException("Escrow result never validates"); + } + + @Override + final public boolean hasRecipient() { + return true; + } + + @Override + final public boolean isSigned() { + return false; + } + }; + + public final static TransactionType SUBSCRIPTION_SUBSCRIBE = new AdvancedPayment() { + + @Override + public final byte getSubtype() { + return TransactionType.SUBTYPE_ADVANCED_PAYMENT_SUBSCRIPTION_SUBSCRIBE; + } + + @Override + Attachment.AdvancedPaymentSubscriptionSubscribe parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { + return new Attachment.AdvancedPaymentSubscriptionSubscribe(buffer, transactionVersion); + } + + @Override + Attachment.AdvancedPaymentSubscriptionSubscribe parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { + return new Attachment.AdvancedPaymentSubscriptionSubscribe(attachmentData); + } + + @Override + final boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { + return true; + } + + @Override + final void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { + Attachment.AdvancedPaymentSubscriptionSubscribe attachment = (Attachment.AdvancedPaymentSubscriptionSubscribe) transaction.getAttachment(); + Subscription.addSubscription(senderAccount, recipientAccount, transaction.getId(), transaction.getAmountNQT(), transaction.getTimestamp(), attachment.getFrequency()); + } + + @Override + final void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { + } + + @Override + boolean isDuplicate(Transaction transaction, Map> duplicates) { + return false; + } + + @Override + void validateAttachment(Transaction transaction) throws NxtException.ValidationException { + Attachment.AdvancedPaymentSubscriptionSubscribe attachment = (Attachment.AdvancedPaymentSubscriptionSubscribe) transaction.getAttachment(); + if(attachment.getFrequency() == null || + attachment.getFrequency().intValue() < Constants.BURST_SUBSCRIPTION_MIN_FREQ || + attachment.getFrequency().intValue() > Constants.BURST_SUBSCRIPTION_MAX_FREQ) { + throw new NxtException.NotValidException("Invalid subscription frequency"); + } + if(transaction.getAmountNQT() < Constants.ONE_NXT || transaction.getAmountNQT() > Constants.MAX_BALANCE_NQT) { + throw new NxtException.NotValidException("Subscriptions must be at least one burst"); + } + if(transaction.getSenderId() == transaction.getRecipientId()) { + throw new NxtException.NotValidException("Cannot create subscription to same address"); + } + if(!Subscription.isEnabled()) { + throw new NxtException.NotYetEnabledException("Subscriptions not yet enabled"); + } + } + + @Override + final public boolean hasRecipient() { + return true; + } + }; + + public final static TransactionType SUBSCRIPTION_CANCEL = new AdvancedPayment() { + + @Override + public final byte getSubtype() { + return TransactionType.SUBTYPE_ADVANCED_PAYMENT_SUBSCRIPTION_CANCEL; + } + + @Override + Attachment.AdvancedPaymentSubscriptionCancel parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { + return new Attachment.AdvancedPaymentSubscriptionCancel(buffer, transactionVersion); + } + + @Override + Attachment.AdvancedPaymentSubscriptionCancel parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { + return new Attachment.AdvancedPaymentSubscriptionCancel(attachmentData); + } + + @Override + final boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { + Attachment.AdvancedPaymentSubscriptionCancel attachment = (Attachment.AdvancedPaymentSubscriptionCancel) transaction.getAttachment(); + Subscription.addRemoval(attachment.getSubscriptionId()); + return true; + } + + @Override + final void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { + Attachment.AdvancedPaymentSubscriptionCancel attachment = (Attachment.AdvancedPaymentSubscriptionCancel) transaction.getAttachment(); + Subscription.removeSubscription(attachment.getSubscriptionId()); + } + + @Override + final void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { + } + + @Override + boolean isDuplicate(Transaction transaction, Map> duplicates) { + Attachment.AdvancedPaymentSubscriptionCancel attachment = (Attachment.AdvancedPaymentSubscriptionCancel) transaction.getAttachment(); + return isDuplicate(AdvancedPayment.SUBSCRIPTION_CANCEL, Convert.toUnsignedLong(attachment.getSubscriptionId()), duplicates); + } + + @Override + void validateAttachment(Transaction transaction) throws NxtException.ValidationException { + Attachment.AdvancedPaymentSubscriptionCancel attachment = (Attachment.AdvancedPaymentSubscriptionCancel) transaction.getAttachment(); + if(attachment.getSubscriptionId() == null) { + throw new NxtException.NotValidException("Subscription cancel must include subscription id"); + } + + Subscription subscription = Subscription.getSubscription(attachment.getSubscriptionId()); + if(subscription == null) { + throw new NxtException.NotValidException("Subscription cancel must contain current subscription id"); + } + + if(!subscription.getSenderId().equals(transaction.getSenderId()) && + !subscription.getRecipientId().equals(transaction.getSenderId())) { + throw new NxtException.NotValidException("Subscription cancel can only be done by participants"); + } + + if(!Subscription.isEnabled()) { + throw new NxtException.NotYetEnabledException("Subscription cancel not yet enabled"); + } + } + + @Override + final public boolean hasRecipient() { + return false; + } + }; + + public final static TransactionType SUBSCRIPTION_PAYMENT = new AdvancedPayment() { + + @Override + public final byte getSubtype() { + return TransactionType.SUBTYPE_ADVANCED_PAYMENT_SUBSCRIPTION_PAYMENT; + } + + @Override + Attachment.AdvancedPaymentSubscriptionPayment parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { + return new Attachment.AdvancedPaymentSubscriptionPayment(buffer, transactionVersion); + } + + @Override + Attachment.AdvancedPaymentSubscriptionPayment parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { + return new Attachment.AdvancedPaymentSubscriptionPayment(attachmentData); + } + + @Override + final boolean applyAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { + return false; + } + + @Override + final void applyAttachment(Transaction transaction, Account senderAccount, Account recipientAccount) { + } + + @Override + final void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount) { + } + + @Override + boolean isDuplicate(Transaction transaction, Map> duplicates) { + return true; + } + + @Override + void validateAttachment(Transaction transaction) throws NxtException.ValidationException { + throw new NxtException.NotValidException("Subscription payment never validates"); + } + + @Override + final public boolean hasRecipient() { + return true; + } + + @Override + final public boolean isSigned() { + return false; + } + }; + } + + public static abstract class AutomatedTransactions extends TransactionType{ + private AutomatedTransactions() { + + } + + @Override + public final byte getType(){ + return TransactionType.TYPE_AUTOMATED_TRANSACTIONS; + } + + @Override + boolean applyAttachmentUnconfirmed(Transaction transaction,Account senderAccount){ + return true; + } + + @Override + void undoAttachmentUnconfirmed(Transaction transaction, Account senderAccount){ + + } + + @Override + final void validateAttachment(Transaction transaction) throws NxtException.ValidationException { + if (transaction.getAmountNQT() != 0) { + throw new NxtException.NotValidException("Invalid automated transaction transaction"); + } + doValidateAttachment(transaction); + } + + abstract void doValidateAttachment(Transaction transaction) throws NxtException.ValidationException; + + + public static final TransactionType AUTOMATED_TRANSACTION_CREATION = new AutomatedTransactions(){ + + @Override + public byte getSubtype() { + return TransactionType.SUBTYPE_AT_CREATION; + } + + @Override + AbstractAttachment parseAttachment(ByteBuffer buffer, + byte transactionVersion) throws NotValidException { + // TODO Auto-generated method stub + //System.out.println("parsing byte AT attachment"); + AutomatedTransactionsCreation attachment = new Attachment.AutomatedTransactionsCreation(buffer,transactionVersion); + //System.out.println("byte AT attachment parsed"); + return attachment; + } + + @Override + AbstractAttachment parseAttachment(JSONObject attachmentData) + throws NotValidException { + // TODO Auto-generated method stub + //System.out.println("parsing at attachment"); + Attachment.AutomatedTransactionsCreation atCreateAttachment = new Attachment.AutomatedTransactionsCreation(attachmentData); + //System.out.println("attachment parsed"); + return atCreateAttachment; + } + + @Override + void doValidateAttachment(Transaction transaction) + throws ValidationException { + //System.out.println("validating attachment"); + if (Nxt.getBlockchain().getLastBlock().getHeight()< Constants.AUTOMATED_TRANSACTION_BLOCK){ + throw new NxtException.NotYetEnabledException("Automated Transactions not yet enabled at height " + Nxt.getBlockchain().getLastBlock().getHeight()); + } + if (transaction.getSignature() != null && Account.getAccount(transaction.getId()) != null) { + Account existingAccount = Account.getAccount(transaction.getId()); + if(existingAccount.getPublicKey() != null && !Arrays.equals(existingAccount.getPublicKey(), new byte[32])) + throw new NxtException.NotValidException("Account with id already exists"); + } + Attachment.AutomatedTransactionsCreation attachment = (Attachment.AutomatedTransactionsCreation) transaction.getAttachment(); + long totalPages = 0; + try { + totalPages = AT_Controller.checkCreationBytes(attachment.getCreationBytes(), Nxt.getBlockchain().getHeight()); + } + catch(AT_Exception e) { + throw new NxtException.NotCurrentlyValidException("Invalid AT creation bytes", e); + } + long requiredFee = totalPages * AT_Constants.getInstance().COST_PER_PAGE( transaction.getHeight() ); + if (transaction.getFeeNQT() < requiredFee){ + throw new NxtException.NotValidException("Insufficient fee for AT creation. Minimum: " + Convert.toUnsignedLong(requiredFee / Constants.ONE_NXT)); + } + if(Nxt.getBlockchain().getHeight() >= Constants.AT_FIX_BLOCK_3) { + if(attachment.getName().length() > Constants.MAX_AUTOMATED_TRANSACTION_NAME_LENGTH) { + throw new NxtException.NotValidException("Name of automated transaction over size limit"); + } + if(attachment.getDescription().length() > Constants.MAX_AUTOMATED_TRANSACTION_DESCRIPTION_LENGTH) { + throw new NxtException.NotValidException("Description of automated transaction over size limit"); + } + } + //System.out.println("validating success"); + } + + @Override + void applyAttachment(Transaction transaction, + Account senderAccount, Account recipientAccount) { + // TODO Auto-generated method stub + Attachment.AutomatedTransactionsCreation attachment = (Attachment.AutomatedTransactionsCreation) transaction.getAttachment(); + Long atId = transaction.getId(); + //System.out.println("Applying AT attachent"); + AT.addAT( transaction.getId() , transaction.getSenderId() , attachment.getName() , attachment.getDescription() , attachment.getCreationBytes() , transaction.getHeight() ); + //System.out.println("At with id "+atId+" successfully applied"); + } + + + @Override + public boolean hasRecipient() { + // TODO Auto-generated method stub + return false; + } + }; + + public static final TransactionType AT_PAYMENT = new AutomatedTransactions() { + @Override + public final byte getSubtype() { + return TransactionType.SUBTYPE_AT_NXT_PAYMENT; + } + + @Override + AbstractAttachment parseAttachment(ByteBuffer buffer, byte transactionVersion) throws NxtException.NotValidException { + return Attachment.AT_PAYMENT; + } + + @Override + AbstractAttachment parseAttachment(JSONObject attachmentData) throws NxtException.NotValidException { + return Attachment.AT_PAYMENT; + } + + @Override + void doValidateAttachment(Transaction transaction) throws NxtException.ValidationException { + /*if (transaction.getAmountNQT() <= 0 || transaction.getAmountNQT() >= Constants.MAX_BALANCE_NQT) { + throw new NxtException.NotValidException("Invalid ordinary payment"); + }*/ + throw new NxtException.NotValidException("AT payment never validates"); + } + + @Override + void applyAttachment(Transaction transaction, + Account senderAccount, Account recipientAccount) { + // TODO Auto-generated method stub + + } + + + @Override + public boolean hasRecipient() { + return true; + } + + @Override + final public boolean isSigned() { + return false; + } + }; + + } + + long minimumFeeNQT(int height, int appendagesSize) { + if (height < BASELINE_FEE_HEIGHT) { + return 0; // No need to validate fees before baseline block + } + Fee fee; + if (height >= NEXT_FEE_HEIGHT) { + fee = getNextFee(); + } else { + fee = getBaselineFee(); + } + return Convert.safeAdd(fee.getConstantFee(), Convert.safeMultiply(appendagesSize, fee.getAppendagesFee())); + } + + protected Fee getBaselineFee() { + return BASELINE_FEE; + } + + protected Fee getNextFee() { + return NEXT_FEE; + } + + public static final class Fee { + private final long constantFee; + private final long appendagesFee; + + public Fee(long constantFee, long appendagesFee) { + this.constantFee = constantFee; + this.appendagesFee = appendagesFee; + } + + public long getConstantFee() { + return constantFee; + } + + public long getAppendagesFee() { + return appendagesFee; + } + + @Override + public String toString() { + return "Fee{" + + "constantFee=" + constantFee + + ", appendagesFee=" + appendagesFee + + '}'; + } + } + +} diff --git a/src/java/nxt/at/AT_API_Impl.java b/src/java/nxt/at/AT_API_Impl.java index e21f75dad..e1564c963 100644 --- a/src/java/nxt/at/AT_API_Impl.java +++ b/src/java/nxt/at/AT_API_Impl.java @@ -1,813 +1,813 @@ -/* - * Copyright (c) 2014 CIYAM Developers - - Distributed under the MIT/X11 software license, please refer to the file license.txt - in the root project directory or http://www.opensource.org/licenses/mit-license.php. - - */ - -package nxt.at; - -import java.math.BigInteger; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Arrays; - -import nxt.Constants; -import nxt.util.Convert; -import fr.cryptohash.RIPEMD160; - - - -public class AT_API_Impl implements AT_API -{ - - AT_API_Platform_Impl platform = AT_API_Platform_Impl.getInstance(); - - - @Override - public long get_A1( AT_Machine_State state ) { - return AT_API_Helper.getLong( state.get_A1() ); - } - - @Override - public long get_A2( AT_Machine_State state ) { - return AT_API_Helper.getLong( state.get_A2() ); - } - - @Override - public long get_A3( AT_Machine_State state ) { - return AT_API_Helper.getLong( state.get_A3() ); - } - - @Override - public long get_A4( AT_Machine_State state ) { - return AT_API_Helper.getLong( state.get_A4() ); - } - - @Override - public long get_B1( AT_Machine_State state ) { - return AT_API_Helper.getLong( state.get_B1() ); - } - - @Override - public long get_B2( AT_Machine_State state ) { - return AT_API_Helper.getLong( state.get_B2() ); - } - - @Override - public long get_B3( AT_Machine_State state ) { - return AT_API_Helper.getLong( state.get_B3() ); - } - - @Override - public long get_B4( AT_Machine_State state ) { - return AT_API_Helper.getLong( state.get_B4() ); - } - - @Override - public void set_A1( long val , AT_Machine_State state ) { - state.set_A1( AT_API_Helper.getByteArray( val ) ); - } - - @Override - public void set_A2( long val , AT_Machine_State state ) { - state.set_A2( AT_API_Helper.getByteArray( val ) ); - } - - @Override - public void set_A3( long val , AT_Machine_State state ) { - state.set_A3( AT_API_Helper.getByteArray( val ) ); - } - - @Override - public void set_A4( long val , AT_Machine_State state ) { - state.set_A4( AT_API_Helper.getByteArray( val ) ); - } - - @Override - public void set_A1_A2( long val1 , long val2 , AT_Machine_State state ) { - state.set_A1( AT_API_Helper.getByteArray( val1 ) ); - state.set_A2( AT_API_Helper.getByteArray( val2 ) ); - } - - @Override - public void set_A3_A4( long val1 , long val2 ,AT_Machine_State state ) { - state.set_A3( AT_API_Helper.getByteArray( val1 ) ); - state.set_A4( AT_API_Helper.getByteArray( val2 ) ); - - } - - @Override - public void set_B1( long val , AT_Machine_State state ) { - state.set_B1( AT_API_Helper.getByteArray( val ) ); - } - - @Override - public void set_B2( long val , AT_Machine_State state ) { - state.set_B2( AT_API_Helper.getByteArray( val ) ); - } - - @Override - public void set_B3( long val , AT_Machine_State state ) { - state.set_B3( AT_API_Helper.getByteArray( val ) ); - } - - @Override - public void set_B4( long val , AT_Machine_State state ) { - state.set_B4( AT_API_Helper.getByteArray( val ) ); - } - - @Override - public void set_B1_B2( long val1 , long val2 , AT_Machine_State state ) { - state.set_B1( AT_API_Helper.getByteArray( val1 ) ); - state.set_B2( AT_API_Helper.getByteArray( val2 ) ); - } - - @Override - public void set_B3_B4( long val3 , long val4 , AT_Machine_State state ) { - state.set_B3( AT_API_Helper.getByteArray( val3 ) ); - state.set_B4( AT_API_Helper.getByteArray( val4 ) ); - } - - @Override - public void clear_A( AT_Machine_State state ) { - byte[] b = new byte[ 8 ]; - state.set_A1( b ); - state.set_A2( b ); - state.set_A3( b ); - state.set_A4( b ); - } - - @Override - public void clear_B( AT_Machine_State state ) { - byte[] b = new byte[ 8 ]; - state.set_B1( b ); - state.set_B2( b ); - state.set_B3( b ); - state.set_B4( b ); - } - - @Override - public void copy_A_From_B( AT_Machine_State state ) { - state.set_A1( state.get_B1() ); - state.set_A2( state.get_B2() ); - state.set_A3( state.get_B3() ); - state.set_A4( state.get_B4() ); - } - - @Override - public void copy_B_From_A( AT_Machine_State state ) { - state.set_B1( state.get_A1() ); - state.set_B2( state.get_A2() ); - state.set_B3( state.get_A3() ); - state.set_B4( state.get_A4() ); - } - - @Override - public long check_A_Is_Zero( AT_Machine_State state ) { - byte[] b = new byte[ 8 ]; - return ( Arrays.equals( state.get_A1() , b ) && - Arrays.equals( state.get_A2() , b ) && - Arrays.equals( state.get_A3() , b ) && - Arrays.equals( state.get_A4() , b ) ) ? 0 : 1 ; - } - - @Override - public long check_B_Is_Zero( AT_Machine_State state ) { - byte[] b = new byte[ 8 ]; - return ( Arrays.equals( state.get_B1() , b ) && - Arrays.equals( state.get_B2() , b ) && - Arrays.equals( state.get_B3() , b ) && - Arrays.equals( state.get_B4() , b ) ) ? 0 : 1 ; - } - - public long check_A_equals_B( AT_Machine_State state ) { - return ( Arrays.equals( state.get_A1() , state.get_B1() ) && - Arrays.equals( state.get_A2() , state.get_B2() ) && - Arrays.equals( state.get_A3() , state.get_B3() ) && - Arrays.equals( state.get_A4() , state.get_B4() ) ) ? 1 : 0; - } - - @Override - public void swap_A_and_B( AT_Machine_State state ) { - byte[] b = new byte[ 8 ]; - - b = state.get_A1().clone(); - state.set_A1( state.get_B1() ); - state.set_B1( b ); - - b = state.get_A2().clone(); - state.set_A2( state.get_B2() ); - state.set_B2( b ); - - b = state.get_A3().clone(); - state.set_A3( state.get_B3() ); - state.set_B3( b ); - - b = state.get_A4().clone(); - state.set_A4( state.get_B4() ); - state.set_B4( b ); - - } - - @Override - public void add_A_to_B( AT_Machine_State state ) { - BigInteger a = AT_API_Helper.getBigInteger(state.get_A1(), state.get_A2(), state.get_A3(), state.get_A4()); - BigInteger b = AT_API_Helper.getBigInteger(state.get_B1(), state.get_B2(), state.get_B3(), state.get_B4()); - BigInteger result = a.add(b); - ByteBuffer resultBuffer = ByteBuffer.wrap(AT_API_Helper.getByteArray(result)); - resultBuffer.order(ByteOrder.LITTLE_ENDIAN); - - byte[] temp = new byte[8]; - resultBuffer.get(temp, 0, 8); - state.set_B1(temp); - resultBuffer.get(temp, 0, 8); - state.set_B2(temp); - resultBuffer.get(temp, 0, 8); - state.set_B3(temp); - resultBuffer.get(temp, 0, 8); - state.set_B4(temp); - } - - @Override - public void add_B_to_A( AT_Machine_State state ) { - BigInteger a = AT_API_Helper.getBigInteger(state.get_A1(), state.get_A2(), state.get_A3(), state.get_A4()); - BigInteger b = AT_API_Helper.getBigInteger(state.get_B1(), state.get_B2(), state.get_B3(), state.get_B4()); - BigInteger result = a.add(b); - ByteBuffer resultBuffer = ByteBuffer.wrap(AT_API_Helper.getByteArray(result)); - resultBuffer.order(ByteOrder.LITTLE_ENDIAN); - - byte[] temp = new byte[8]; - resultBuffer.get(temp, 0, 8); - state.set_A1(temp); - resultBuffer.get(temp, 0, 8); - state.set_A2(temp); - resultBuffer.get(temp, 0, 8); - state.set_A3(temp); - resultBuffer.get(temp, 0, 8); - state.set_A4(temp); - } - - @Override - public void sub_A_from_B( AT_Machine_State state ) { - BigInteger a = AT_API_Helper.getBigInteger(state.get_A1(), state.get_A2(), state.get_A3(), state.get_A4()); - BigInteger b = AT_API_Helper.getBigInteger(state.get_B1(), state.get_B2(), state.get_B3(), state.get_B4()); - BigInteger result = b.subtract(a); - ByteBuffer resultBuffer = ByteBuffer.wrap(AT_API_Helper.getByteArray(result)); - resultBuffer.order(ByteOrder.LITTLE_ENDIAN); - - byte[] temp = new byte[8]; - resultBuffer.get(temp, 0, 8); - state.set_B1(temp); - resultBuffer.get(temp, 0, 8); - state.set_B2(temp); - resultBuffer.get(temp, 0, 8); - state.set_B3(temp); - resultBuffer.get(temp, 0, 8); - state.set_B4(temp); - } - - @Override - public void sub_B_from_A( AT_Machine_State state ) { - BigInteger a = AT_API_Helper.getBigInteger(state.get_A1(), state.get_A2(), state.get_A3(), state.get_A4()); - BigInteger b = AT_API_Helper.getBigInteger(state.get_B1(), state.get_B2(), state.get_B3(), state.get_B4()); - BigInteger result = a.subtract(b); - ByteBuffer resultBuffer = ByteBuffer.wrap(AT_API_Helper.getByteArray(result)); - resultBuffer.order(ByteOrder.LITTLE_ENDIAN); - - byte[] temp = new byte[8]; - resultBuffer.get(temp, 0, 8); - state.set_A1(temp); - resultBuffer.get(temp, 0, 8); - state.set_A2(temp); - resultBuffer.get(temp, 0, 8); - state.set_A3(temp); - resultBuffer.get(temp, 0, 8); - state.set_A4(temp); - } - - @Override - public void mul_A_by_B( AT_Machine_State state ) { - BigInteger a = AT_API_Helper.getBigInteger(state.get_A1(), state.get_A2(), state.get_A3(), state.get_A4()); - BigInteger b = AT_API_Helper.getBigInteger(state.get_B1(), state.get_B2(), state.get_B3(), state.get_B4()); - BigInteger result = a.multiply(b); - ByteBuffer resultBuffer = ByteBuffer.wrap(AT_API_Helper.getByteArray(result)); - resultBuffer.order(ByteOrder.LITTLE_ENDIAN); - - byte[] temp = new byte[8]; - resultBuffer.get(temp, 0, 8); - state.set_B1(temp); - resultBuffer.get(temp, 0, 8); - state.set_B2(temp); - resultBuffer.get(temp, 0, 8); - state.set_B3(temp); - resultBuffer.get(temp, 0, 8); - state.set_B4(temp); - } - - @Override - public void mul_B_by_A( AT_Machine_State state ) { - BigInteger a = AT_API_Helper.getBigInteger(state.get_A1(), state.get_A2(), state.get_A3(), state.get_A4()); - BigInteger b = AT_API_Helper.getBigInteger(state.get_B1(), state.get_B2(), state.get_B3(), state.get_B4()); - BigInteger result = a.multiply(b); - ByteBuffer resultBuffer = ByteBuffer.wrap(AT_API_Helper.getByteArray(result)); - resultBuffer.order(ByteOrder.LITTLE_ENDIAN); - - byte[] temp = new byte[8]; - resultBuffer.get(temp, 0, 8); - state.set_A1(temp); - resultBuffer.get(temp, 0, 8); - state.set_A2(temp); - resultBuffer.get(temp, 0, 8); - state.set_A3(temp); - resultBuffer.get(temp, 0, 8); - state.set_A4(temp); - } - - @Override - public void div_A_by_B( AT_Machine_State state ) { - BigInteger a = AT_API_Helper.getBigInteger(state.get_A1(), state.get_A2(), state.get_A3(), state.get_A4()); - BigInteger b = AT_API_Helper.getBigInteger(state.get_B1(), state.get_B2(), state.get_B3(), state.get_B4()); - if(b.compareTo(BigInteger.ZERO) == 0) - return; - BigInteger result = a.divide(b); - ByteBuffer resultBuffer = ByteBuffer.wrap(AT_API_Helper.getByteArray(result)); - resultBuffer.order(ByteOrder.LITTLE_ENDIAN); - - byte[] temp = new byte[8]; - resultBuffer.get(temp, 0, 8); - state.set_B1(temp); - resultBuffer.get(temp, 0, 8); - state.set_B2(temp); - resultBuffer.get(temp, 0, 8); - state.set_B3(temp); - resultBuffer.get(temp, 0, 8); - state.set_B4(temp); - } - - @Override - public void div_B_by_A( AT_Machine_State state ) { - BigInteger a = AT_API_Helper.getBigInteger(state.get_A1(), state.get_A2(), state.get_A3(), state.get_A4()); - BigInteger b = AT_API_Helper.getBigInteger(state.get_B1(), state.get_B2(), state.get_B3(), state.get_B4()); - if(a.compareTo(BigInteger.ZERO) == 0) - return; - BigInteger result = b.divide(a); - ByteBuffer resultBuffer = ByteBuffer.wrap(AT_API_Helper.getByteArray(result)); - resultBuffer.order(ByteOrder.LITTLE_ENDIAN); - - byte[] temp = new byte[8]; - resultBuffer.get(temp, 0, 8); - state.set_A1(temp); - resultBuffer.get(temp, 0, 8); - state.set_A2(temp); - resultBuffer.get(temp, 0, 8); - state.set_A3(temp); - resultBuffer.get(temp, 0, 8); - state.set_A4(temp); - } - - @Override - public void or_A_with_B ( AT_Machine_State state ) { - ByteBuffer a = ByteBuffer.allocate(32); - a.order( ByteOrder.LITTLE_ENDIAN ); - a.put(state.get_A1()); - a.put(state.get_A2()); - a.put(state.get_A3()); - a.put(state.get_A4()); - a.clear(); - - ByteBuffer b = ByteBuffer.allocate(32); - b.order( ByteOrder.LITTLE_ENDIAN ); - b.put(state.get_B1()); - b.put(state.get_B2()); - b.put(state.get_B3()); - b.put(state.get_B4()); - b.clear(); - - state.set_A1(AT_API_Helper.getByteArray(a.getLong(0) | b.getLong(0))); - state.set_A2(AT_API_Helper.getByteArray(a.getLong(8) | b.getLong(8))); - state.set_A3(AT_API_Helper.getByteArray(a.getLong(16) | b.getLong(16))); - state.set_A4(AT_API_Helper.getByteArray(a.getLong(24) | b.getLong(24))); - } - - @Override - public void or_B_with_A ( AT_Machine_State state ) { - ByteBuffer a = ByteBuffer.allocate(32); - a.order( ByteOrder.LITTLE_ENDIAN ); - a.put(state.get_A1()); - a.put(state.get_A2()); - a.put(state.get_A3()); - a.put(state.get_A4()); - a.clear(); - - ByteBuffer b = ByteBuffer.allocate(32); - b.order( ByteOrder.LITTLE_ENDIAN ); - b.put(state.get_B1()); - b.put(state.get_B2()); - b.put(state.get_B3()); - b.put(state.get_B4()); - b.clear(); - - state.set_B1(AT_API_Helper.getByteArray(a.getLong(0) | b.getLong(0))); - state.set_B2(AT_API_Helper.getByteArray(a.getLong(8) | b.getLong(8))); - state.set_B3(AT_API_Helper.getByteArray(a.getLong(16) | b.getLong(16))); - state.set_B4(AT_API_Helper.getByteArray(a.getLong(24) | b.getLong(24))); - } - - @Override - public void and_A_with_B ( AT_Machine_State state ) { - ByteBuffer a = ByteBuffer.allocate(32); - a.order( ByteOrder.LITTLE_ENDIAN ); - a.put(state.get_A1()); - a.put(state.get_A2()); - a.put(state.get_A3()); - a.put(state.get_A4()); - a.clear(); - - ByteBuffer b = ByteBuffer.allocate(32); - b.order( ByteOrder.LITTLE_ENDIAN ); - b.put(state.get_B1()); - b.put(state.get_B2()); - b.put(state.get_B3()); - b.put(state.get_B4()); - b.clear(); - - state.set_A1(AT_API_Helper.getByteArray(a.getLong(0) & b.getLong(0))); - state.set_A2(AT_API_Helper.getByteArray(a.getLong(8) & b.getLong(8))); - state.set_A3(AT_API_Helper.getByteArray(a.getLong(16) & b.getLong(16))); - state.set_A4(AT_API_Helper.getByteArray(a.getLong(24) & b.getLong(24))); - } - - @Override - public void and_B_with_A ( AT_Machine_State state ) { - ByteBuffer a = ByteBuffer.allocate(32); - a.order( ByteOrder.LITTLE_ENDIAN ); - a.put(state.get_A1()); - a.put(state.get_A2()); - a.put(state.get_A3()); - a.put(state.get_A4()); - a.clear(); - - ByteBuffer b = ByteBuffer.allocate(32); - b.order( ByteOrder.LITTLE_ENDIAN ); - b.put(state.get_B1()); - b.put(state.get_B2()); - b.put(state.get_B3()); - b.put(state.get_B4()); - b.clear(); - - state.set_B1(AT_API_Helper.getByteArray(a.getLong(0) & b.getLong(0))); - state.set_B2(AT_API_Helper.getByteArray(a.getLong(8) & b.getLong(8))); - state.set_B3(AT_API_Helper.getByteArray(a.getLong(16) & b.getLong(16))); - state.set_B4(AT_API_Helper.getByteArray(a.getLong(24) & b.getLong(24))); - } - - @Override - public void xor_A_with_B ( AT_Machine_State state ) { - ByteBuffer a = ByteBuffer.allocate(32); - a.order( ByteOrder.LITTLE_ENDIAN ); - a.put(state.get_A1()); - a.put(state.get_A2()); - a.put(state.get_A3()); - a.put(state.get_A4()); - a.clear(); - - ByteBuffer b = ByteBuffer.allocate(32); - b.order( ByteOrder.LITTLE_ENDIAN ); - b.put(state.get_B1()); - b.put(state.get_B2()); - b.put(state.get_B3()); - b.put(state.get_B4()); - b.clear(); - - state.set_A1(AT_API_Helper.getByteArray(a.getLong(0) ^ b.getLong(0))); - state.set_A2(AT_API_Helper.getByteArray(a.getLong(8) ^ b.getLong(8))); - state.set_A3(AT_API_Helper.getByteArray(a.getLong(16) ^ b.getLong(16))); - state.set_A4(AT_API_Helper.getByteArray(a.getLong(24) ^ b.getLong(24))); - } - - @Override - public void xor_B_with_A ( AT_Machine_State state ) { - ByteBuffer a = ByteBuffer.allocate(32); - a.order( ByteOrder.LITTLE_ENDIAN ); - a.put(state.get_A1()); - a.put(state.get_A2()); - a.put(state.get_A3()); - a.put(state.get_A4()); - a.clear(); - - ByteBuffer b = ByteBuffer.allocate(32); - b.order( ByteOrder.LITTLE_ENDIAN ); - b.put(state.get_B1()); - b.put(state.get_B2()); - b.put(state.get_B3()); - b.put(state.get_B4()); - b.clear(); - - state.set_B1(AT_API_Helper.getByteArray(a.getLong(0) ^ b.getLong(0))); - state.set_B2(AT_API_Helper.getByteArray(a.getLong(8) ^ b.getLong(8))); - state.set_B3(AT_API_Helper.getByteArray(a.getLong(16) ^ b.getLong(16))); - state.set_B4(AT_API_Helper.getByteArray(a.getLong(24) ^ b.getLong(24))); - } - - @Override - public void MD5_A_to_B( AT_Machine_State state ) { - ByteBuffer b = ByteBuffer.allocate( 16 ); - b.order( ByteOrder.LITTLE_ENDIAN ); - - b.put( state.get_A1() ); - b.put( state.get_A2() ); - - try { - MessageDigest md5 = MessageDigest.getInstance("MD5"); - ByteBuffer mdb = ByteBuffer.wrap( md5.digest( b.array() ) ); - mdb.order( ByteOrder.LITTLE_ENDIAN ); - - state.set_B1( AT_API_Helper.getByteArray( mdb.getLong(0) ) ); - state.set_B1( AT_API_Helper.getByteArray( mdb.getLong(8) ) ); - - } catch (NoSuchAlgorithmException e) { - //not expected to reach that point - e.printStackTrace(); - } - } - - - @Override - public long check_MD5_A_with_B( AT_Machine_State state ) { - if ( state.getHeight() >= Constants.AT_FIX_BLOCK_3 ) { - ByteBuffer b = ByteBuffer.allocate( 16 ); - b.order( ByteOrder.LITTLE_ENDIAN ); - - b.put( state.get_A1() ); - b.put( state.get_A2() ); - - try { - MessageDigest md5 = MessageDigest.getInstance("MD5"); - ByteBuffer mdb = ByteBuffer.wrap( md5.digest( b.array() ) ); - mdb.order( ByteOrder.LITTLE_ENDIAN ); - - return ( mdb.getLong(0) == AT_API_Helper.getLong( state.get_B1() ) && - mdb.getLong(8) == AT_API_Helper.getLong( state.get_B2() ) ) ? 1 : 0; - } catch (NoSuchAlgorithmException e) { - //not expected to reach that point - e.printStackTrace(); - throw new RuntimeException("Failed to check md5"); - } - } - else { - return ( Arrays.equals( state.get_A1() , state.get_B1() ) && - Arrays.equals( state.get_A2() , state.get_B2() ) ) ? 1 : 0; - } - } - - @Override - public void HASH160_A_to_B( AT_Machine_State state ) { - ByteBuffer b = ByteBuffer.allocate(32); - b.order(ByteOrder.LITTLE_ENDIAN); - - b.put(state.get_A1()); - b.put(state.get_A2()); - b.put(state.get_A3()); - b.put(state.get_A4()); - - RIPEMD160 ripemd160 = new RIPEMD160(); - ByteBuffer ripemdb = ByteBuffer.wrap(ripemd160.digest(b.array())); - ripemdb.order(ByteOrder.LITTLE_ENDIAN); - - state.set_B1(AT_API_Helper.getByteArray(ripemdb.getLong(0))); - state.set_B2(AT_API_Helper.getByteArray(ripemdb.getLong(8))); - state.set_B3(AT_API_Helper.getByteArray((long)ripemdb.getInt(16))); - - } - - @Override - public long check_HASH160_A_with_B( AT_Machine_State state ) { - if ( state.getHeight() >= Constants.AT_FIX_BLOCK_3 ) { - ByteBuffer b = ByteBuffer.allocate( 32 ); - b.order( ByteOrder.LITTLE_ENDIAN ); - - b.put( state.get_A1() ); - b.put( state.get_A2() ); - b.put( state.get_A3() ); - b.put( state.get_A4() ); - - RIPEMD160 ripemd160 = new RIPEMD160(); - ByteBuffer ripemdb = ByteBuffer.wrap( ripemd160.digest( b.array() ) ); - ripemdb.order( ByteOrder.LITTLE_ENDIAN ); - - return ( ripemdb.getLong(0) == AT_API_Helper.getLong( state.get_B1() ) && - ripemdb.getLong(8) == AT_API_Helper.getLong( state.get_B2() ) && - ripemdb.getInt(16) == ((int)(AT_API_Helper.getLong( state.get_B3() ) & 0x00000000FFFFFFFFL )) - ) ? 1 : 0; - } - else { - return(Arrays.equals(state.get_A1(), state.get_B1()) && - Arrays.equals(state.get_A2(), state.get_B2()) && - (AT_API_Helper.getLong(state.get_A3()) & 0x00000000FFFFFFFFL) == (AT_API_Helper.getLong(state.get_B3()) & 0x00000000FFFFFFFFL)) ? 1 : 0; - } - } - - @Override - public void SHA256_A_to_B( AT_Machine_State state ) { - ByteBuffer b = ByteBuffer.allocate( 32 ); - b.order( ByteOrder.LITTLE_ENDIAN ); - - b.put( state.get_A1() ); - b.put( state.get_A2() ); - b.put( state.get_A3() ); - b.put( state.get_A4() ); - - try { - MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); - ByteBuffer shab = ByteBuffer.wrap( sha256.digest( b.array() ) ); - shab.order( ByteOrder.LITTLE_ENDIAN ); - - state.set_B1( AT_API_Helper.getByteArray( shab.getLong( 0 ) ) ); - state.set_B2( AT_API_Helper.getByteArray( shab.getLong( 8 ) ) ); - state.set_B3( AT_API_Helper.getByteArray( shab.getLong( 16 ) ) ); - state.set_B4( AT_API_Helper.getByteArray( shab.getLong( 24 ) ) ); - - } catch (NoSuchAlgorithmException e) { - //not expected to reach that point - e.printStackTrace(); - } - } - - @Override - public long check_SHA256_A_with_B( AT_Machine_State state ) { - if ( state.getHeight() >= Constants.AT_FIX_BLOCK_3 ) { - ByteBuffer b = ByteBuffer.allocate(32); - b.order( ByteOrder.LITTLE_ENDIAN ); - - b.put( state.get_A1() ); - b.put( state.get_A2() ); - b.put( state.get_A3() ); - b.put( state.get_A4() ); - - try { - MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); - ByteBuffer shab = ByteBuffer.wrap( sha256.digest( b.array() ) ); - shab.order( ByteOrder.LITTLE_ENDIAN ); - - return ( shab.getLong(0) == AT_API_Helper.getLong( state.get_B1() ) && - shab.getLong(8) == AT_API_Helper.getLong( state.get_B2() ) && - shab.getLong(16) == AT_API_Helper.getLong( state.get_B3() ) && - shab.getLong(24) == AT_API_Helper.getLong( state.get_B4() ) ) ? 1 : 0; - } catch (NoSuchAlgorithmException e) { - //not expected to reach that point - e.printStackTrace(); - throw new RuntimeException("Failed to check sha256"); - } - } - else { - return ( Arrays.equals( state.get_A1() , state.get_B1() ) && - Arrays.equals( state.get_A2() , state.get_B2() ) && - Arrays.equals( state.get_A3() , state.get_B3() ) && - Arrays.equals( state.get_A4() , state.get_B4() )) ? 1 : 0; - } - } - - @Override - public long get_Block_Timestamp( AT_Machine_State state ) { - return platform.get_Block_Timestamp( state ); - - } - - @Override - public long get_Creation_Timestamp( AT_Machine_State state ) { - return platform.get_Creation_Timestamp( state ); - } - - @Override - public long get_Last_Block_Timestamp( AT_Machine_State state ) { - return platform.get_Last_Block_Timestamp( state ); - } - - @Override - public void put_Last_Block_Hash_In_A( AT_Machine_State state ) { - platform.put_Last_Block_Hash_In_A( state ); - - } - - @Override - public void A_to_Tx_after_Timestamp( long val , AT_Machine_State state ) { - platform.A_to_Tx_after_Timestamp( val , state ); - - } - - @Override - public long get_Type_for_Tx_in_A( AT_Machine_State state ) { - return platform.get_Type_for_Tx_in_A( state ); - } - - @Override - public long get_Amount_for_Tx_in_A( AT_Machine_State state ) { - return platform.get_Amount_for_Tx_in_A( state ); - } - - @Override - public long get_Timestamp_for_Tx_in_A( AT_Machine_State state ) { - return platform.get_Timestamp_for_Tx_in_A( state ); - } - - @Override - public long get_Random_Id_for_Tx_in_A( AT_Machine_State state ) { - return platform.get_Random_Id_for_Tx_in_A( state ); - } - - @Override - public void message_from_Tx_in_A_to_B( AT_Machine_State state ) { - platform.message_from_Tx_in_A_to_B( state ); - } - - @Override - public void B_to_Address_of_Tx_in_A( AT_Machine_State state ) { - - platform.B_to_Address_of_Tx_in_A( state ); - } - - @Override - public void B_to_Address_of_Creator( AT_Machine_State state ) { - platform.B_to_Address_of_Creator( state ); - - } - - @Override - public long get_Current_Balance( AT_Machine_State state ) { - return platform.get_Current_Balance( state ); - } - - @Override - public long get_Previous_Balance( AT_Machine_State state ) { - return platform.get_Previous_Balance( state ); - } - - @Override - public void send_to_Address_in_B( long val , AT_Machine_State state ) { - platform.send_to_Address_in_B( val , state ); - } - - @Override - public void send_All_to_Address_in_B( AT_Machine_State state ) { - platform.send_All_to_Address_in_B( state ); - } - - @Override - public void send_Old_to_Address_in_B( AT_Machine_State state ) { - platform.send_Old_to_Address_in_B( state ); - } - - @Override - public void send_A_to_Address_in_B( AT_Machine_State state ) { - platform.send_A_to_Address_in_B( state ); - } - - @Override - public long add_Minutes_to_Timestamp( long val1 , long val2 , AT_Machine_State state ) { - return platform.add_Minutes_to_Timestamp( val1 , val2 , state ); - } - - @Override - public void set_Min_Activation_Amount( long val , AT_Machine_State state ) { - state.setMinActivationAmount(val); - } - - @Override - public void put_Last_Block_Generation_Signature_In_A( AT_Machine_State state ) { - platform.put_Last_Block_Generation_Signature_In_A( state ); - } - - @Override - public void SHA256_to_B( long val1 , long val2 , AT_Machine_State state ) { - if(val1 < 0 || val2 < 0 || - (val1 + val2 - 1) < 0 || - ((long)val1)*8+8>((long)Integer.MAX_VALUE) || - val1*8+8>state.getDsize() || - ((long)val1 + (long)val2 - 1)*8+8>((long)Integer.MAX_VALUE) || - (val1 + val2 - 1)*8+8>state.getDsize()) - { - return; - } - - try { - MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); - sha256.update(state.getAp_data().array(), (int)val1, (int)(val2 > 256 ? 256 : val2)); - ByteBuffer shab = ByteBuffer.wrap( sha256.digest() ); - shab.order( ByteOrder.LITTLE_ENDIAN ); - - state.set_B1( AT_API_Helper.getByteArray( shab.getLong( 0 ) ) ); - state.set_B2( AT_API_Helper.getByteArray( shab.getLong( 8 ) ) ); - state.set_B3( AT_API_Helper.getByteArray( shab.getLong( 16 ) ) ); - state.set_B4( AT_API_Helper.getByteArray( shab.getLong( 24 ) ) ); - - } catch (NoSuchAlgorithmException e) { - //not expected to reach that point - e.printStackTrace(); - } - } - +/* + * Copyright (c) 2014 CIYAM Developers + + Distributed under the MIT/X11 software license, please refer to the file license.txt + in the root project directory or http://www.opensource.org/licenses/mit-license.php. + + */ + +package nxt.at; + +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; + +import nxt.Constants; +import nxt.util.Convert; +import fr.cryptohash.RIPEMD160; + + + +public class AT_API_Impl implements AT_API +{ + + AT_API_Platform_Impl platform = AT_API_Platform_Impl.getInstance(); + + + @Override + public long get_A1( AT_Machine_State state ) { + return AT_API_Helper.getLong( state.get_A1() ); + } + + @Override + public long get_A2( AT_Machine_State state ) { + return AT_API_Helper.getLong( state.get_A2() ); + } + + @Override + public long get_A3( AT_Machine_State state ) { + return AT_API_Helper.getLong( state.get_A3() ); + } + + @Override + public long get_A4( AT_Machine_State state ) { + return AT_API_Helper.getLong( state.get_A4() ); + } + + @Override + public long get_B1( AT_Machine_State state ) { + return AT_API_Helper.getLong( state.get_B1() ); + } + + @Override + public long get_B2( AT_Machine_State state ) { + return AT_API_Helper.getLong( state.get_B2() ); + } + + @Override + public long get_B3( AT_Machine_State state ) { + return AT_API_Helper.getLong( state.get_B3() ); + } + + @Override + public long get_B4( AT_Machine_State state ) { + return AT_API_Helper.getLong( state.get_B4() ); + } + + @Override + public void set_A1( long val , AT_Machine_State state ) { + state.set_A1( AT_API_Helper.getByteArray( val ) ); + } + + @Override + public void set_A2( long val , AT_Machine_State state ) { + state.set_A2( AT_API_Helper.getByteArray( val ) ); + } + + @Override + public void set_A3( long val , AT_Machine_State state ) { + state.set_A3( AT_API_Helper.getByteArray( val ) ); + } + + @Override + public void set_A4( long val , AT_Machine_State state ) { + state.set_A4( AT_API_Helper.getByteArray( val ) ); + } + + @Override + public void set_A1_A2( long val1 , long val2 , AT_Machine_State state ) { + state.set_A1( AT_API_Helper.getByteArray( val1 ) ); + state.set_A2( AT_API_Helper.getByteArray( val2 ) ); + } + + @Override + public void set_A3_A4( long val1 , long val2 ,AT_Machine_State state ) { + state.set_A3( AT_API_Helper.getByteArray( val1 ) ); + state.set_A4( AT_API_Helper.getByteArray( val2 ) ); + + } + + @Override + public void set_B1( long val , AT_Machine_State state ) { + state.set_B1( AT_API_Helper.getByteArray( val ) ); + } + + @Override + public void set_B2( long val , AT_Machine_State state ) { + state.set_B2( AT_API_Helper.getByteArray( val ) ); + } + + @Override + public void set_B3( long val , AT_Machine_State state ) { + state.set_B3( AT_API_Helper.getByteArray( val ) ); + } + + @Override + public void set_B4( long val , AT_Machine_State state ) { + state.set_B4( AT_API_Helper.getByteArray( val ) ); + } + + @Override + public void set_B1_B2( long val1 , long val2 , AT_Machine_State state ) { + state.set_B1( AT_API_Helper.getByteArray( val1 ) ); + state.set_B2( AT_API_Helper.getByteArray( val2 ) ); + } + + @Override + public void set_B3_B4( long val3 , long val4 , AT_Machine_State state ) { + state.set_B3( AT_API_Helper.getByteArray( val3 ) ); + state.set_B4( AT_API_Helper.getByteArray( val4 ) ); + } + + @Override + public void clear_A( AT_Machine_State state ) { + byte[] b = new byte[ 8 ]; + state.set_A1( b ); + state.set_A2( b ); + state.set_A3( b ); + state.set_A4( b ); + } + + @Override + public void clear_B( AT_Machine_State state ) { + byte[] b = new byte[ 8 ]; + state.set_B1( b ); + state.set_B2( b ); + state.set_B3( b ); + state.set_B4( b ); + } + + @Override + public void copy_A_From_B( AT_Machine_State state ) { + state.set_A1( state.get_B1() ); + state.set_A2( state.get_B2() ); + state.set_A3( state.get_B3() ); + state.set_A4( state.get_B4() ); + } + + @Override + public void copy_B_From_A( AT_Machine_State state ) { + state.set_B1( state.get_A1() ); + state.set_B2( state.get_A2() ); + state.set_B3( state.get_A3() ); + state.set_B4( state.get_A4() ); + } + + @Override + public long check_A_Is_Zero( AT_Machine_State state ) { + byte[] b = new byte[ 8 ]; + return ( Arrays.equals( state.get_A1() , b ) && + Arrays.equals( state.get_A2() , b ) && + Arrays.equals( state.get_A3() , b ) && + Arrays.equals( state.get_A4() , b ) ) ? 0 : 1 ; + } + + @Override + public long check_B_Is_Zero( AT_Machine_State state ) { + byte[] b = new byte[ 8 ]; + return ( Arrays.equals( state.get_B1() , b ) && + Arrays.equals( state.get_B2() , b ) && + Arrays.equals( state.get_B3() , b ) && + Arrays.equals( state.get_B4() , b ) ) ? 0 : 1 ; + } + + public long check_A_equals_B( AT_Machine_State state ) { + return ( Arrays.equals( state.get_A1() , state.get_B1() ) && + Arrays.equals( state.get_A2() , state.get_B2() ) && + Arrays.equals( state.get_A3() , state.get_B3() ) && + Arrays.equals( state.get_A4() , state.get_B4() ) ) ? 1 : 0; + } + + @Override + public void swap_A_and_B( AT_Machine_State state ) { + byte[] b = new byte[ 8 ]; + + b = state.get_A1().clone(); + state.set_A1( state.get_B1() ); + state.set_B1( b ); + + b = state.get_A2().clone(); + state.set_A2( state.get_B2() ); + state.set_B2( b ); + + b = state.get_A3().clone(); + state.set_A3( state.get_B3() ); + state.set_B3( b ); + + b = state.get_A4().clone(); + state.set_A4( state.get_B4() ); + state.set_B4( b ); + + } + + @Override + public void add_A_to_B( AT_Machine_State state ) { + BigInteger a = AT_API_Helper.getBigInteger(state.get_A1(), state.get_A2(), state.get_A3(), state.get_A4()); + BigInteger b = AT_API_Helper.getBigInteger(state.get_B1(), state.get_B2(), state.get_B3(), state.get_B4()); + BigInteger result = a.add(b); + ByteBuffer resultBuffer = ByteBuffer.wrap(AT_API_Helper.getByteArray(result)); + resultBuffer.order(ByteOrder.LITTLE_ENDIAN); + + byte[] temp = new byte[8]; + resultBuffer.get(temp, 0, 8); + state.set_B1(temp); + resultBuffer.get(temp, 0, 8); + state.set_B2(temp); + resultBuffer.get(temp, 0, 8); + state.set_B3(temp); + resultBuffer.get(temp, 0, 8); + state.set_B4(temp); + } + + @Override + public void add_B_to_A( AT_Machine_State state ) { + BigInteger a = AT_API_Helper.getBigInteger(state.get_A1(), state.get_A2(), state.get_A3(), state.get_A4()); + BigInteger b = AT_API_Helper.getBigInteger(state.get_B1(), state.get_B2(), state.get_B3(), state.get_B4()); + BigInteger result = a.add(b); + ByteBuffer resultBuffer = ByteBuffer.wrap(AT_API_Helper.getByteArray(result)); + resultBuffer.order(ByteOrder.LITTLE_ENDIAN); + + byte[] temp = new byte[8]; + resultBuffer.get(temp, 0, 8); + state.set_A1(temp); + resultBuffer.get(temp, 0, 8); + state.set_A2(temp); + resultBuffer.get(temp, 0, 8); + state.set_A3(temp); + resultBuffer.get(temp, 0, 8); + state.set_A4(temp); + } + + @Override + public void sub_A_from_B( AT_Machine_State state ) { + BigInteger a = AT_API_Helper.getBigInteger(state.get_A1(), state.get_A2(), state.get_A3(), state.get_A4()); + BigInteger b = AT_API_Helper.getBigInteger(state.get_B1(), state.get_B2(), state.get_B3(), state.get_B4()); + BigInteger result = b.subtract(a); + ByteBuffer resultBuffer = ByteBuffer.wrap(AT_API_Helper.getByteArray(result)); + resultBuffer.order(ByteOrder.LITTLE_ENDIAN); + + byte[] temp = new byte[8]; + resultBuffer.get(temp, 0, 8); + state.set_B1(temp); + resultBuffer.get(temp, 0, 8); + state.set_B2(temp); + resultBuffer.get(temp, 0, 8); + state.set_B3(temp); + resultBuffer.get(temp, 0, 8); + state.set_B4(temp); + } + + @Override + public void sub_B_from_A( AT_Machine_State state ) { + BigInteger a = AT_API_Helper.getBigInteger(state.get_A1(), state.get_A2(), state.get_A3(), state.get_A4()); + BigInteger b = AT_API_Helper.getBigInteger(state.get_B1(), state.get_B2(), state.get_B3(), state.get_B4()); + BigInteger result = a.subtract(b); + ByteBuffer resultBuffer = ByteBuffer.wrap(AT_API_Helper.getByteArray(result)); + resultBuffer.order(ByteOrder.LITTLE_ENDIAN); + + byte[] temp = new byte[8]; + resultBuffer.get(temp, 0, 8); + state.set_A1(temp); + resultBuffer.get(temp, 0, 8); + state.set_A2(temp); + resultBuffer.get(temp, 0, 8); + state.set_A3(temp); + resultBuffer.get(temp, 0, 8); + state.set_A4(temp); + } + + @Override + public void mul_A_by_B( AT_Machine_State state ) { + BigInteger a = AT_API_Helper.getBigInteger(state.get_A1(), state.get_A2(), state.get_A3(), state.get_A4()); + BigInteger b = AT_API_Helper.getBigInteger(state.get_B1(), state.get_B2(), state.get_B3(), state.get_B4()); + BigInteger result = a.multiply(b); + ByteBuffer resultBuffer = ByteBuffer.wrap(AT_API_Helper.getByteArray(result)); + resultBuffer.order(ByteOrder.LITTLE_ENDIAN); + + byte[] temp = new byte[8]; + resultBuffer.get(temp, 0, 8); + state.set_B1(temp); + resultBuffer.get(temp, 0, 8); + state.set_B2(temp); + resultBuffer.get(temp, 0, 8); + state.set_B3(temp); + resultBuffer.get(temp, 0, 8); + state.set_B4(temp); + } + + @Override + public void mul_B_by_A( AT_Machine_State state ) { + BigInteger a = AT_API_Helper.getBigInteger(state.get_A1(), state.get_A2(), state.get_A3(), state.get_A4()); + BigInteger b = AT_API_Helper.getBigInteger(state.get_B1(), state.get_B2(), state.get_B3(), state.get_B4()); + BigInteger result = a.multiply(b); + ByteBuffer resultBuffer = ByteBuffer.wrap(AT_API_Helper.getByteArray(result)); + resultBuffer.order(ByteOrder.LITTLE_ENDIAN); + + byte[] temp = new byte[8]; + resultBuffer.get(temp, 0, 8); + state.set_A1(temp); + resultBuffer.get(temp, 0, 8); + state.set_A2(temp); + resultBuffer.get(temp, 0, 8); + state.set_A3(temp); + resultBuffer.get(temp, 0, 8); + state.set_A4(temp); + } + + @Override + public void div_A_by_B( AT_Machine_State state ) { + BigInteger a = AT_API_Helper.getBigInteger(state.get_A1(), state.get_A2(), state.get_A3(), state.get_A4()); + BigInteger b = AT_API_Helper.getBigInteger(state.get_B1(), state.get_B2(), state.get_B3(), state.get_B4()); + if(b.compareTo(BigInteger.ZERO) == 0) + return; + BigInteger result = a.divide(b); + ByteBuffer resultBuffer = ByteBuffer.wrap(AT_API_Helper.getByteArray(result)); + resultBuffer.order(ByteOrder.LITTLE_ENDIAN); + + byte[] temp = new byte[8]; + resultBuffer.get(temp, 0, 8); + state.set_B1(temp); + resultBuffer.get(temp, 0, 8); + state.set_B2(temp); + resultBuffer.get(temp, 0, 8); + state.set_B3(temp); + resultBuffer.get(temp, 0, 8); + state.set_B4(temp); + } + + @Override + public void div_B_by_A( AT_Machine_State state ) { + BigInteger a = AT_API_Helper.getBigInteger(state.get_A1(), state.get_A2(), state.get_A3(), state.get_A4()); + BigInteger b = AT_API_Helper.getBigInteger(state.get_B1(), state.get_B2(), state.get_B3(), state.get_B4()); + if(a.compareTo(BigInteger.ZERO) == 0) + return; + BigInteger result = b.divide(a); + ByteBuffer resultBuffer = ByteBuffer.wrap(AT_API_Helper.getByteArray(result)); + resultBuffer.order(ByteOrder.LITTLE_ENDIAN); + + byte[] temp = new byte[8]; + resultBuffer.get(temp, 0, 8); + state.set_A1(temp); + resultBuffer.get(temp, 0, 8); + state.set_A2(temp); + resultBuffer.get(temp, 0, 8); + state.set_A3(temp); + resultBuffer.get(temp, 0, 8); + state.set_A4(temp); + } + + @Override + public void or_A_with_B ( AT_Machine_State state ) { + ByteBuffer a = ByteBuffer.allocate(32); + a.order( ByteOrder.LITTLE_ENDIAN ); + a.put(state.get_A1()); + a.put(state.get_A2()); + a.put(state.get_A3()); + a.put(state.get_A4()); + a.clear(); + + ByteBuffer b = ByteBuffer.allocate(32); + b.order( ByteOrder.LITTLE_ENDIAN ); + b.put(state.get_B1()); + b.put(state.get_B2()); + b.put(state.get_B3()); + b.put(state.get_B4()); + b.clear(); + + state.set_A1(AT_API_Helper.getByteArray(a.getLong(0) | b.getLong(0))); + state.set_A2(AT_API_Helper.getByteArray(a.getLong(8) | b.getLong(8))); + state.set_A3(AT_API_Helper.getByteArray(a.getLong(16) | b.getLong(16))); + state.set_A4(AT_API_Helper.getByteArray(a.getLong(24) | b.getLong(24))); + } + + @Override + public void or_B_with_A ( AT_Machine_State state ) { + ByteBuffer a = ByteBuffer.allocate(32); + a.order( ByteOrder.LITTLE_ENDIAN ); + a.put(state.get_A1()); + a.put(state.get_A2()); + a.put(state.get_A3()); + a.put(state.get_A4()); + a.clear(); + + ByteBuffer b = ByteBuffer.allocate(32); + b.order( ByteOrder.LITTLE_ENDIAN ); + b.put(state.get_B1()); + b.put(state.get_B2()); + b.put(state.get_B3()); + b.put(state.get_B4()); + b.clear(); + + state.set_B1(AT_API_Helper.getByteArray(a.getLong(0) | b.getLong(0))); + state.set_B2(AT_API_Helper.getByteArray(a.getLong(8) | b.getLong(8))); + state.set_B3(AT_API_Helper.getByteArray(a.getLong(16) | b.getLong(16))); + state.set_B4(AT_API_Helper.getByteArray(a.getLong(24) | b.getLong(24))); + } + + @Override + public void and_A_with_B ( AT_Machine_State state ) { + ByteBuffer a = ByteBuffer.allocate(32); + a.order( ByteOrder.LITTLE_ENDIAN ); + a.put(state.get_A1()); + a.put(state.get_A2()); + a.put(state.get_A3()); + a.put(state.get_A4()); + a.clear(); + + ByteBuffer b = ByteBuffer.allocate(32); + b.order( ByteOrder.LITTLE_ENDIAN ); + b.put(state.get_B1()); + b.put(state.get_B2()); + b.put(state.get_B3()); + b.put(state.get_B4()); + b.clear(); + + state.set_A1(AT_API_Helper.getByteArray(a.getLong(0) & b.getLong(0))); + state.set_A2(AT_API_Helper.getByteArray(a.getLong(8) & b.getLong(8))); + state.set_A3(AT_API_Helper.getByteArray(a.getLong(16) & b.getLong(16))); + state.set_A4(AT_API_Helper.getByteArray(a.getLong(24) & b.getLong(24))); + } + + @Override + public void and_B_with_A ( AT_Machine_State state ) { + ByteBuffer a = ByteBuffer.allocate(32); + a.order( ByteOrder.LITTLE_ENDIAN ); + a.put(state.get_A1()); + a.put(state.get_A2()); + a.put(state.get_A3()); + a.put(state.get_A4()); + a.clear(); + + ByteBuffer b = ByteBuffer.allocate(32); + b.order( ByteOrder.LITTLE_ENDIAN ); + b.put(state.get_B1()); + b.put(state.get_B2()); + b.put(state.get_B3()); + b.put(state.get_B4()); + b.clear(); + + state.set_B1(AT_API_Helper.getByteArray(a.getLong(0) & b.getLong(0))); + state.set_B2(AT_API_Helper.getByteArray(a.getLong(8) & b.getLong(8))); + state.set_B3(AT_API_Helper.getByteArray(a.getLong(16) & b.getLong(16))); + state.set_B4(AT_API_Helper.getByteArray(a.getLong(24) & b.getLong(24))); + } + + @Override + public void xor_A_with_B ( AT_Machine_State state ) { + ByteBuffer a = ByteBuffer.allocate(32); + a.order( ByteOrder.LITTLE_ENDIAN ); + a.put(state.get_A1()); + a.put(state.get_A2()); + a.put(state.get_A3()); + a.put(state.get_A4()); + a.clear(); + + ByteBuffer b = ByteBuffer.allocate(32); + b.order( ByteOrder.LITTLE_ENDIAN ); + b.put(state.get_B1()); + b.put(state.get_B2()); + b.put(state.get_B3()); + b.put(state.get_B4()); + b.clear(); + + state.set_A1(AT_API_Helper.getByteArray(a.getLong(0) ^ b.getLong(0))); + state.set_A2(AT_API_Helper.getByteArray(a.getLong(8) ^ b.getLong(8))); + state.set_A3(AT_API_Helper.getByteArray(a.getLong(16) ^ b.getLong(16))); + state.set_A4(AT_API_Helper.getByteArray(a.getLong(24) ^ b.getLong(24))); + } + + @Override + public void xor_B_with_A ( AT_Machine_State state ) { + ByteBuffer a = ByteBuffer.allocate(32); + a.order( ByteOrder.LITTLE_ENDIAN ); + a.put(state.get_A1()); + a.put(state.get_A2()); + a.put(state.get_A3()); + a.put(state.get_A4()); + a.clear(); + + ByteBuffer b = ByteBuffer.allocate(32); + b.order( ByteOrder.LITTLE_ENDIAN ); + b.put(state.get_B1()); + b.put(state.get_B2()); + b.put(state.get_B3()); + b.put(state.get_B4()); + b.clear(); + + state.set_B1(AT_API_Helper.getByteArray(a.getLong(0) ^ b.getLong(0))); + state.set_B2(AT_API_Helper.getByteArray(a.getLong(8) ^ b.getLong(8))); + state.set_B3(AT_API_Helper.getByteArray(a.getLong(16) ^ b.getLong(16))); + state.set_B4(AT_API_Helper.getByteArray(a.getLong(24) ^ b.getLong(24))); + } + + @Override + public void MD5_A_to_B( AT_Machine_State state ) { + ByteBuffer b = ByteBuffer.allocate( 16 ); + b.order( ByteOrder.LITTLE_ENDIAN ); + + b.put( state.get_A1() ); + b.put( state.get_A2() ); + + try { + MessageDigest md5 = MessageDigest.getInstance("MD5"); + ByteBuffer mdb = ByteBuffer.wrap( md5.digest( b.array() ) ); + mdb.order( ByteOrder.LITTLE_ENDIAN ); + + state.set_B1( AT_API_Helper.getByteArray( mdb.getLong(0) ) ); + state.set_B1( AT_API_Helper.getByteArray( mdb.getLong(8) ) ); + + } catch (NoSuchAlgorithmException e) { + //not expected to reach that point + e.printStackTrace(); + } + } + + + @Override + public long check_MD5_A_with_B( AT_Machine_State state ) { + if ( state.getHeight() >= Constants.AT_FIX_BLOCK_3 ) { + ByteBuffer b = ByteBuffer.allocate( 16 ); + b.order( ByteOrder.LITTLE_ENDIAN ); + + b.put( state.get_A1() ); + b.put( state.get_A2() ); + + try { + MessageDigest md5 = MessageDigest.getInstance("MD5"); + ByteBuffer mdb = ByteBuffer.wrap( md5.digest( b.array() ) ); + mdb.order( ByteOrder.LITTLE_ENDIAN ); + + return ( mdb.getLong(0) == AT_API_Helper.getLong( state.get_B1() ) && + mdb.getLong(8) == AT_API_Helper.getLong( state.get_B2() ) ) ? 1 : 0; + } catch (NoSuchAlgorithmException e) { + //not expected to reach that point + e.printStackTrace(); + throw new RuntimeException("Failed to check md5"); + } + } + else { + return ( Arrays.equals( state.get_A1() , state.get_B1() ) && + Arrays.equals( state.get_A2() , state.get_B2() ) ) ? 1 : 0; + } + } + + @Override + public void HASH160_A_to_B( AT_Machine_State state ) { + ByteBuffer b = ByteBuffer.allocate(32); + b.order(ByteOrder.LITTLE_ENDIAN); + + b.put(state.get_A1()); + b.put(state.get_A2()); + b.put(state.get_A3()); + b.put(state.get_A4()); + + RIPEMD160 ripemd160 = new RIPEMD160(); + ByteBuffer ripemdb = ByteBuffer.wrap(ripemd160.digest(b.array())); + ripemdb.order(ByteOrder.LITTLE_ENDIAN); + + state.set_B1(AT_API_Helper.getByteArray(ripemdb.getLong(0))); + state.set_B2(AT_API_Helper.getByteArray(ripemdb.getLong(8))); + state.set_B3(AT_API_Helper.getByteArray((long)ripemdb.getInt(16))); + + } + + @Override + public long check_HASH160_A_with_B( AT_Machine_State state ) { + if ( state.getHeight() >= Constants.AT_FIX_BLOCK_3 ) { + ByteBuffer b = ByteBuffer.allocate( 32 ); + b.order( ByteOrder.LITTLE_ENDIAN ); + + b.put( state.get_A1() ); + b.put( state.get_A2() ); + b.put( state.get_A3() ); + b.put( state.get_A4() ); + + RIPEMD160 ripemd160 = new RIPEMD160(); + ByteBuffer ripemdb = ByteBuffer.wrap( ripemd160.digest( b.array() ) ); + ripemdb.order( ByteOrder.LITTLE_ENDIAN ); + + return ( ripemdb.getLong(0) == AT_API_Helper.getLong( state.get_B1() ) && + ripemdb.getLong(8) == AT_API_Helper.getLong( state.get_B2() ) && + ripemdb.getInt(16) == ((int)(AT_API_Helper.getLong( state.get_B3() ) & 0x00000000FFFFFFFFL )) + ) ? 1 : 0; + } + else { + return(Arrays.equals(state.get_A1(), state.get_B1()) && + Arrays.equals(state.get_A2(), state.get_B2()) && + (AT_API_Helper.getLong(state.get_A3()) & 0x00000000FFFFFFFFL) == (AT_API_Helper.getLong(state.get_B3()) & 0x00000000FFFFFFFFL)) ? 1 : 0; + } + } + + @Override + public void SHA256_A_to_B( AT_Machine_State state ) { + ByteBuffer b = ByteBuffer.allocate( 32 ); + b.order( ByteOrder.LITTLE_ENDIAN ); + + b.put( state.get_A1() ); + b.put( state.get_A2() ); + b.put( state.get_A3() ); + b.put( state.get_A4() ); + + try { + MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); + ByteBuffer shab = ByteBuffer.wrap( sha256.digest( b.array() ) ); + shab.order( ByteOrder.LITTLE_ENDIAN ); + + state.set_B1( AT_API_Helper.getByteArray( shab.getLong( 0 ) ) ); + state.set_B2( AT_API_Helper.getByteArray( shab.getLong( 8 ) ) ); + state.set_B3( AT_API_Helper.getByteArray( shab.getLong( 16 ) ) ); + state.set_B4( AT_API_Helper.getByteArray( shab.getLong( 24 ) ) ); + + } catch (NoSuchAlgorithmException e) { + //not expected to reach that point + e.printStackTrace(); + } + } + + @Override + public long check_SHA256_A_with_B( AT_Machine_State state ) { + if ( state.getHeight() >= Constants.AT_FIX_BLOCK_3 ) { + ByteBuffer b = ByteBuffer.allocate(32); + b.order( ByteOrder.LITTLE_ENDIAN ); + + b.put( state.get_A1() ); + b.put( state.get_A2() ); + b.put( state.get_A3() ); + b.put( state.get_A4() ); + + try { + MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); + ByteBuffer shab = ByteBuffer.wrap( sha256.digest( b.array() ) ); + shab.order( ByteOrder.LITTLE_ENDIAN ); + + return ( shab.getLong(0) == AT_API_Helper.getLong( state.get_B1() ) && + shab.getLong(8) == AT_API_Helper.getLong( state.get_B2() ) && + shab.getLong(16) == AT_API_Helper.getLong( state.get_B3() ) && + shab.getLong(24) == AT_API_Helper.getLong( state.get_B4() ) ) ? 1 : 0; + } catch (NoSuchAlgorithmException e) { + //not expected to reach that point + e.printStackTrace(); + throw new RuntimeException("Failed to check sha256"); + } + } + else { + return ( Arrays.equals( state.get_A1() , state.get_B1() ) && + Arrays.equals( state.get_A2() , state.get_B2() ) && + Arrays.equals( state.get_A3() , state.get_B3() ) && + Arrays.equals( state.get_A4() , state.get_B4() )) ? 1 : 0; + } + } + + @Override + public long get_Block_Timestamp( AT_Machine_State state ) { + return platform.get_Block_Timestamp( state ); + + } + + @Override + public long get_Creation_Timestamp( AT_Machine_State state ) { + return platform.get_Creation_Timestamp( state ); + } + + @Override + public long get_Last_Block_Timestamp( AT_Machine_State state ) { + return platform.get_Last_Block_Timestamp( state ); + } + + @Override + public void put_Last_Block_Hash_In_A( AT_Machine_State state ) { + platform.put_Last_Block_Hash_In_A( state ); + + } + + @Override + public void A_to_Tx_after_Timestamp( long val , AT_Machine_State state ) { + platform.A_to_Tx_after_Timestamp( val , state ); + + } + + @Override + public long get_Type_for_Tx_in_A( AT_Machine_State state ) { + return platform.get_Type_for_Tx_in_A( state ); + } + + @Override + public long get_Amount_for_Tx_in_A( AT_Machine_State state ) { + return platform.get_Amount_for_Tx_in_A( state ); + } + + @Override + public long get_Timestamp_for_Tx_in_A( AT_Machine_State state ) { + return platform.get_Timestamp_for_Tx_in_A( state ); + } + + @Override + public long get_Random_Id_for_Tx_in_A( AT_Machine_State state ) { + return platform.get_Random_Id_for_Tx_in_A( state ); + } + + @Override + public void message_from_Tx_in_A_to_B( AT_Machine_State state ) { + platform.message_from_Tx_in_A_to_B( state ); + } + + @Override + public void B_to_Address_of_Tx_in_A( AT_Machine_State state ) { + + platform.B_to_Address_of_Tx_in_A( state ); + } + + @Override + public void B_to_Address_of_Creator( AT_Machine_State state ) { + platform.B_to_Address_of_Creator( state ); + + } + + @Override + public long get_Current_Balance( AT_Machine_State state ) { + return platform.get_Current_Balance( state ); + } + + @Override + public long get_Previous_Balance( AT_Machine_State state ) { + return platform.get_Previous_Balance( state ); + } + + @Override + public void send_to_Address_in_B( long val , AT_Machine_State state ) { + platform.send_to_Address_in_B( val , state ); + } + + @Override + public void send_All_to_Address_in_B( AT_Machine_State state ) { + platform.send_All_to_Address_in_B( state ); + } + + @Override + public void send_Old_to_Address_in_B( AT_Machine_State state ) { + platform.send_Old_to_Address_in_B( state ); + } + + @Override + public void send_A_to_Address_in_B( AT_Machine_State state ) { + platform.send_A_to_Address_in_B( state ); + } + + @Override + public long add_Minutes_to_Timestamp( long val1 , long val2 , AT_Machine_State state ) { + return platform.add_Minutes_to_Timestamp( val1 , val2 , state ); + } + + @Override + public void set_Min_Activation_Amount( long val , AT_Machine_State state ) { + state.setMinActivationAmount(val); + } + + @Override + public void put_Last_Block_Generation_Signature_In_A( AT_Machine_State state ) { + platform.put_Last_Block_Generation_Signature_In_A( state ); + } + + @Override + public void SHA256_to_B( long val1 , long val2 , AT_Machine_State state ) { + if(val1 < 0 || val2 < 0 || + (val1 + val2 - 1) < 0 || + ((long)val1)*8+8>((long)Integer.MAX_VALUE) || + val1*8+8>state.getDsize() || + ((long)val1 + (long)val2 - 1)*8+8>((long)Integer.MAX_VALUE) || + (val1 + val2 - 1)*8+8>state.getDsize()) + { + return; + } + + try { + MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); + sha256.update(state.getAp_data().array(), (int)val1, (int)(val2 > 256 ? 256 : val2)); + ByteBuffer shab = ByteBuffer.wrap( sha256.digest() ); + shab.order( ByteOrder.LITTLE_ENDIAN ); + + state.set_B1( AT_API_Helper.getByteArray( shab.getLong( 0 ) ) ); + state.set_B2( AT_API_Helper.getByteArray( shab.getLong( 8 ) ) ); + state.set_B3( AT_API_Helper.getByteArray( shab.getLong( 16 ) ) ); + state.set_B4( AT_API_Helper.getByteArray( shab.getLong( 24 ) ) ); + + } catch (NoSuchAlgorithmException e) { + //not expected to reach that point + e.printStackTrace(); + } + } + } \ No newline at end of file