diff --git a/.github/workflows/build_and_publish.yaml b/.github/workflows/build_and_publish.yaml new file mode 100644 index 000000000..31df2d19a --- /dev/null +++ b/.github/workflows/build_and_publish.yaml @@ -0,0 +1,34 @@ +name: Build and Release Artifacts + +on: [push, pull_request] + +defaults: + run: + working-directory: ./ + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'adopt' + - name: Build And Test + run: ./gradlew clean build + - name: Publish Artifact + if : ${{contains(github.ref, 'master') }} + run: ./gradlew publish + env: + USERNAME: ${{ github.actor }} + TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/manual.yml b/.github/workflows/manual.yml new file mode 100644 index 000000000..5a393221f --- /dev/null +++ b/.github/workflows/manual.yml @@ -0,0 +1,42 @@ +name: Manual Release +on: + workflow_dispatch: + inputs: + publish_artifacts: + description: Publish artifact + type: boolean + default: false + required: true + artifacts_version: + description: Release artifact version + default: '0.0.1-test' + required: true +defaults: + run: + working-directory: ./ + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + - run: echo ${{github.event.inputs.publish_artifacts == 'true'}} + - uses: actions/checkout@v3 + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'adopt' + - name: Build And Test + run: ./gradlew clean build + - name: Publish Artifact + if: ${{ github.event.inputs.publish_artifacts == 'true'}} + run: ./gradlew publish + env: + USERNAME: ${{ github.actor }} + TOKEN: ${{ secrets.GITHUB_TOKEN }} + VERSION: '${{ github.event.inputs.artifacts_version }}' diff --git a/Minima_wp_v9.pdf b/Minima_wp_v9.pdf new file mode 100644 index 000000000..f40363ec6 Binary files /dev/null and b/Minima_wp_v9.pdf differ diff --git a/build.gradle b/build.gradle index d5757c9ab..475b9e268 100644 --- a/build.gradle +++ b/build.gradle @@ -23,8 +23,16 @@ plugins { id 'com.github.johnrengelman.shadow' version '6.1.0' id 'com.palantir.docker' version '0.28.0' + + id 'maven-publish' } +group 'org.minima' +version '0.100.33' + +sourceCompatibility = '11' +targetCompatibility = '11' + repositories { // Use jcenter for resolving dependencies. // You can declare any Maven/Ivy/file repository here. @@ -101,4 +109,25 @@ docker { files tasks.shadowJar, "$rootDir/scripts/installDapps.sh", "$rootDir/dapps/" copySpec.from("$buildDir/lib/minima-all.jar",).into(".") //"./scripts/installDapps.sh", "./dapps/block.minidapp", "./dapps/wallet.minidapp", "dapps/terminal.minidapp", "dapps/emh.minidapp" -} \ No newline at end of file +} + +publishing { + repositories { + maven { + name = "GitHubPackages" + url = "https://maven.pkg.github.com/minima-global/Minima" + if (System.getenv("VERSION") != '') { + version = System.getenv("VERSION") + } + credentials { + username = System.getenv("USERNAME") + password = System.getenv("TOKEN") + } + } + } + publications { + gpr(MavenPublication) { + from(components.java) + } + } +} diff --git a/jar/minima-all.jar b/jar/minima-all.jar index 52018a719..9381c08a8 100644 Binary files a/jar/minima-all.jar and b/jar/minima-all.jar differ diff --git a/jar/minima.jar b/jar/minima.jar index 52018a719..edf22a0ad 100644 Binary files a/jar/minima.jar and b/jar/minima.jar differ diff --git a/minima_whitepaper_v6.pdf b/minima_whitepaper_v6.pdf deleted file mode 100644 index 0adc0b8c3..000000000 Binary files a/minima_whitepaper_v6.pdf and /dev/null differ diff --git a/src/org/minima/database/MinimaDB.java b/src/org/minima/database/MinimaDB.java index 2d7482238..99c505179 100644 --- a/src/org/minima/database/MinimaDB.java +++ b/src/org/minima/database/MinimaDB.java @@ -231,9 +231,6 @@ public void saveSQL() { readLock(true); try { - //Get the base Database folder - File basedb = getBaseDBFolder(); - //Clean shutdown of SQL DBs mTxPoWDB.saveDB(); mArchive.saveDB(); @@ -250,6 +247,21 @@ public void saveSQL() { readLock(false); } + public void saveWalletSQL() { + //We need read lock + readLock(true); + + try { + mWallet.saveDB(); + + }catch(Exception exc) { + MinimaLogger.log("ERROR saveWalletSQL "+exc); + } + + //Release the krakken + readLock(false); + } + public void saveState() { //We need read lock diff --git a/src/org/minima/database/mmr/MMR.java b/src/org/minima/database/mmr/MMR.java index 2f9deb911..23a715b9d 100644 --- a/src/org/minima/database/mmr/MMR.java +++ b/src/org/minima/database/mmr/MMR.java @@ -9,6 +9,7 @@ import java.util.ArrayList; import java.util.Enumeration; import java.util.Hashtable; +import java.util.Random; import org.minima.objects.base.MiniData; import org.minima.objects.base.MiniNumber; @@ -222,6 +223,11 @@ private void addHashTableEntry(MMREntry zEntry) { mSetEntries.put(name, zEntry); } + private void removeHashTableEntry(MMREntry zEntry) { + String name = getHashTableEntry(zEntry.getRow(), zEntry.getEntryNumber()); + mSetEntries.remove(name); + } + /** * Sets the Entry value in THIS SET ONLY. Does not affect parents. * @param zRow @@ -698,68 +704,83 @@ public static MMR ReadFromStream(DataInputStream zIn) throws IOException { } /** - * TEST STUFF + * PRUNE + * + * Recursive Remove subtrees below a ZERO SUM Value */ - public static void main(String[] zArgs) { + public void pruneTree() { + //Get the Peaks.. + ArrayList peaks = getPeaks(); + for(MMREntry peak : peaks) { + prune(peak); + } + } - MMR mmr = new MMR(); - - //First bit of data - MMRData one = new MMRData(new MiniData("0x01"), new MiniNumber(1)); - MMRData two = new MMRData(new MiniData("0x02"), new MiniNumber(2)); - MMRData three = new MMRData(new MiniData("0x03"), new MiniNumber(3)); - MMRData four = new MMRData(new MiniData("0x04"), new MiniNumber(4)); - - //Add to the tree - mmr.addEntry(one); - printinfo(mmr); - - mmr.addEntry(two); - printinfo(mmr); + private void prune(MMREntry zStartNode) { - mmr.addEntry(one); - printinfo(mmr); - - mmr.addEntry(one); - printinfo(mmr); + //Is this a valid MMRENtry + if(zStartNode.isEmpty()) { + //Already pruned.. + return; + } - //Now get a a proof.. - MMRProof proof = mmr.getProofToPeak(MMREntryNumber.ZERO); - MMRData peak = proof.calculateProof(one); - System.out.println("Peak Proof : "+proof); - System.out.println("Proof : "+peak); + //Which row are the children on.. + int childrow = zStartNode.getChildRow(); + if(childrow<0) { + //We are at the base leaf nodes.. leave it.. + return; + } - boolean check = mmr.checkProof(one, proof); - System.out.println("isValid : "+check); + //The children.. + MMREntry leftchild = getEntry(childrow, zStartNode.getLeftChildEntry()); + MMREntry rightchild = getEntry(childrow, zStartNode.getRightChildEntry()); - //Now get a a proof.. - MMRProof fullproof = mmr.getProof(MMREntryNumber.ZERO); - MMRData fpeak = fullproof.calculateProof(one); - System.out.println("Full Proof : "+fullproof); - System.out.println("Proof : "+fpeak); + //Prune the children if they exist + prune(leftchild); + prune(rightchild); - check = mmr.checkProof(one, fullproof); - System.out.println("isValid : "+check); + //Is this a ZERO node.. if so remove the children + if(zStartNode.getMMRData().getValue().isEqual(MiniNumber.ZERO)) { + removeHashTableEntry(leftchild); + removeHashTableEntry(rightchild); + } - //Now update something.. - mmr.updateEntry(MMREntryNumber.ZERO, proof, three); - printinfo(mmr); + } + + /** + * TEST STUFF + */ + public static void main(String[] zArgs) { + + System.out.println("** MMR Tree Prune POC **"); - //Get the new proof.. - proof = mmr.getProofToPeak(MMREntryNumber.ZERO); + MMR mmr = new MMR(); - //Now create a child MMR.. with the parent - MMR child = new MMR(mmr); - printinfo(child); - child.updateEntry(MMREntryNumber.ZERO, proof, four); - printinfo(child); + //First bit of data + MMRData zero = new MMRData(new MiniData("0x00"), new MiniNumber(0)); + MMRData one = new MMRData(new MiniData("0x01"), new MiniNumber(1)); - //Now create a child MMR.. with the parent - MMR child2 = new MMR(mmr); - printinfo(child2); - child2.clearParent(); - child2.updateEntry(MMREntryNumber.ZERO, proof, four); - printinfo(child2); + //Add 16 entries.. + for(int loop=0;loop<16;loop++) { + mmr.addEntry(one); + } + printmmrtree(mmr); + + //Set random values to Zero.. + for(int zz=0;zz<24;zz++) { + int rand = new Random().nextInt(16); + MMREntryNumber entry = new MMREntryNumber(rand); + MMREntry ent = mmr.getEntry(0, entry); + if(ent.isEmpty() || ent.getMMRData().getValue().isEqual(MiniNumber.ZERO)) { + continue; + } + + System.out.println("\nSet entry "+rand+" to 0"); + MMRProof proof = mmr.getProofToPeak(entry); + mmr.updateEntry(entry, proof, zero); + mmr.pruneTree(); + printmmrtree(mmr); + } } public static void printinfo(MMR zTree) { @@ -777,5 +798,52 @@ public static void printinfo(MMR zTree) { System.out.println("Root : "+root); } + public static void printmmrtree(MMR zTree) { + //Start from the max row.. + int toprow = zTree.mMaxRow; + for(int i=toprow;i>=0;i--) { + + //The start gap + int startgap = (int) (Math.pow(2, i) -1) * 2; + int gap = (int) (Math.pow(2, i+1)) * 2; + + //Get the row.. + ArrayList row = new ArrayList<>(); + Enumeration entries = zTree.mSetEntries.elements(); + while(entries.hasMoreElements()) { + MMREntry entry = entries.nextElement(); + if(entry.getRow() == i) { + row.add(entry); + } + } + + //The final char buffrer for the row + char[] str = new char[128]; + for(int c=0;c<128;c++) { + str[c] = ' '; + } + + StringBuffer strow = new StringBuffer(" "); + for(MMREntry entry : row) { + + //Add the entry to the correct spot.. + int xpos = entry.getEntryNumber().getBigDecimal().intValue(); + int finalpos = startgap+(xpos*gap); + int value = entry.getMMRData().getValue().getAsInt(); + String valstr = ""+value; + char[] cc = valstr.toCharArray(); + + System.arraycopy(cc, 0, str, finalpos, cc.length); + } + + System.out.println(str); + } + + //Print the peak value.. + System.out.println("Total Entries : "+zTree.mSetEntries.size()); + System.out.println("Tree Peak Sum Value : "+zTree.getRoot().getValue()); + + } + } diff --git a/src/org/minima/database/txpowtree/TxPoWTreeNode.java b/src/org/minima/database/txpowtree/TxPoWTreeNode.java index 946446e9e..c162ef65a 100644 --- a/src/org/minima/database/txpowtree/TxPoWTreeNode.java +++ b/src/org/minima/database/txpowtree/TxPoWTreeNode.java @@ -112,7 +112,7 @@ private void constructMMR(boolean zFindRelevant) { //Are we checking for relevant data ArrayList allrel = new ArrayList<>(); if(zFindRelevant) { - allrel = wallet.getAllRelevant(); + allrel = wallet.getAllRelevant(false); } //Add all the peaks.. @@ -333,6 +333,17 @@ public TxPoWTreeNode getParent() { return mParent; } + public TxPoWTreeNode getParent(int zBlocks) { + TxPoWTreeNode parent = this; + int counter = 0; + while(counter coinidcoins = mTransaction.getOutputCoinsWithCoinID(); - JSONArray coinarrfull = new JSONArray(); + ArrayList coinidcoins = mTransaction.getAllOutputs(); JSONArray coinarr = new JSONArray(); for(Coin cc : coinidcoins) { - coinarrfull.add(cc.toJSON()); coinarr.add(MiniData.getMiniDataVersion(cc).to0xString()); } ret.put("outputcoindata", coinarr); diff --git a/src/org/minima/database/wallet/KeyRow.java b/src/org/minima/database/wallet/KeyRow.java index 832d733ea..03f80e3a5 100644 --- a/src/org/minima/database/wallet/KeyRow.java +++ b/src/org/minima/database/wallet/KeyRow.java @@ -1,5 +1,7 @@ package org.minima.database.wallet; +import org.minima.objects.Address; +import org.minima.objects.base.MiniData; import org.minima.utils.json.JSONObject; public class KeyRow { @@ -15,7 +17,7 @@ public KeyRow(String zPrivKey, String zPubKey, String zAddress, String zScript, mPubliKey = zPubKey; mSimpleAddress = zAddress; mScript = zScript; - mTrack = zTrack; + mTrack = zTrack; } public String getPrivateKey() { @@ -44,6 +46,7 @@ public JSONObject toJSON() { ret.put("publickey", getPublicKey()); ret.put("script", getScript()); ret.put("address", getAddress()); + ret.put("miniaddress", Address.makeMinimaAddress(new MiniData(getAddress()))); ret.put("track", mTrack); return ret; diff --git a/src/org/minima/database/wallet/Wallet.java b/src/org/minima/database/wallet/Wallet.java index ca2aaa6a1..a12d3ee73 100644 --- a/src/org/minima/database/wallet/Wallet.java +++ b/src/org/minima/database/wallet/Wallet.java @@ -17,15 +17,21 @@ public class Wallet extends SqlDB { + /** + * How many default keys to create + */ + public static int NUMBER_GETADDRESS_KEYS = 64; + /** * PreparedStatements */ - PreparedStatement SQL_CREATE_PUBLIC_KEY = null; - PreparedStatement SQL_GET_ALL_RELEVANT = null; - PreparedStatement SQL_GET_FADDRESS = null; - PreparedStatement SQL_GET_FPUBLICKEY = null; - PreparedStatement SQL_GET_USES = null; - PreparedStatement SQL_UPDATE_USES = null; + PreparedStatement SQL_CREATE_PUBLIC_KEY = null; + PreparedStatement SQL_GET_ALL_NONSINGLE_RELEVANT = null; + PreparedStatement SQL_GET_ALL_RELEVANT = null; + PreparedStatement SQL_GET_FADDRESS = null; + PreparedStatement SQL_GET_FPUBLICKEY = null; + PreparedStatement SQL_GET_USES = null; + PreparedStatement SQL_UPDATE_USES = null; /** * Scripts DB @@ -38,16 +44,30 @@ public class Wallet extends SqlDB { */ Hashtable mTreeKeys = new Hashtable<>(); + /** + * Stop creating keys if you are + */ + boolean mShuttingdown = false; + /** * Has there been a change to the Key Rows.. otherwise used cached */ - boolean mKeyRowChange = true; - ArrayList mCachedRelevantKeys = new ArrayList<>(); + boolean mKeyRowChange = true; + ArrayList mCachedRelevantNonSingleKeys = new ArrayList<>(); + ArrayList mCachedRelevantAllKeys = new ArrayList<>(); public Wallet() { super(); } + public void shuttiongDown() { + mShuttingdown = true; + } + + private boolean isShuttingDown() { + return mShuttingdown; + } + @Override protected void createSQL() { try { @@ -63,7 +83,8 @@ protected void createSQL() { + " `privatekey` varchar(80) NOT NULL," + " `publickey` varchar(80) NOT NULL," + " `script` varchar(255) NOT NULL," - + " `simpleaddress` varchar(80) NOT NULL" + + " `simpleaddress` varchar(80) NOT NULL," + + " `singleuse` int NOT NULL" + ")"; //Run it.. @@ -84,12 +105,13 @@ protected void createSQL() { stmt.close(); //Create some prepared statements.. - SQL_CREATE_PUBLIC_KEY = mSQLCOnnection.prepareStatement("INSERT IGNORE INTO keys ( basemodifier, uses, privatekey, publickey, script, simpleaddress ) VALUES ( ?, ?, ? ,? ,? ,? )"); - SQL_GET_ALL_RELEVANT = mSQLCOnnection.prepareStatement("SELECT * FROM keys"); - SQL_GET_FADDRESS = mSQLCOnnection.prepareStatement("SELECT * FROM keys WHERE simpleaddress=?"); - SQL_GET_FPUBLICKEY = mSQLCOnnection.prepareStatement("SELECT * FROM keys WHERE publickey=?"); - SQL_UPDATE_USES = mSQLCOnnection.prepareStatement("UPDATE keys SET uses=? WHERE privatekey=?"); - SQL_GET_USES = mSQLCOnnection.prepareStatement("SELECT uses FROM keys WHERE privatekey=?"); + SQL_CREATE_PUBLIC_KEY = mSQLCOnnection.prepareStatement("INSERT IGNORE INTO keys ( basemodifier, uses, privatekey, publickey, script, simpleaddress, singleuse ) VALUES ( ?, ?, ?, ? ,? ,? ,? )"); + SQL_GET_ALL_RELEVANT = mSQLCOnnection.prepareStatement("SELECT * FROM keys"); + SQL_GET_ALL_NONSINGLE_RELEVANT = mSQLCOnnection.prepareStatement("SELECT * FROM keys WHERE singleuse=0"); + SQL_GET_FADDRESS = mSQLCOnnection.prepareStatement("SELECT * FROM keys WHERE simpleaddress=?"); + SQL_GET_FPUBLICKEY = mSQLCOnnection.prepareStatement("SELECT * FROM keys WHERE publickey=?"); + SQL_UPDATE_USES = mSQLCOnnection.prepareStatement("UPDATE keys SET uses=? WHERE privatekey=?"); + SQL_GET_USES = mSQLCOnnection.prepareStatement("SELECT uses FROM keys WHERE privatekey=?"); //ScriptsDB SQL_ADD_CUSTOM_SCRIPT = mSQLCOnnection.prepareStatement("INSERT IGNORE INTO scripts ( script, address, track ) VALUES ( ?, ? , ? )"); @@ -106,31 +128,43 @@ protected void createSQL() { /** * Create an initial set of keys / addresses to use */ - public void initDefaultKeys() { + public boolean initDefaultKeys() { //Get all the keys.. - ArrayList allkeys = getAllRelevant(); + ArrayList allkeys = getAllRelevant(true); + boolean allcreated = false; //Check we have the desired amount.. int numkeys = allkeys.size(); - if(numkeys < 16) { + if(numkeys < NUMBER_GETADDRESS_KEYS) { - MinimaLogger.log("Creating initial key set.."); + //Create a few at a time.. + int diff = NUMBER_GETADDRESS_KEYS - numkeys; + if(diff>8) { + diff = 8; + } - //Create the remaining keys - int create = 16 - numkeys; - for(int i=0;i allkeys = getAllRelevant(); + ArrayList allkeys = getAllRelevant(true); int numkeys = allkeys.size(); //Now pick a random key.. @@ -142,18 +176,18 @@ public KeyRow getKey() { /** * Create a NEW key */ - public synchronized KeyRow createNewKey() { + public synchronized KeyRow createNewKey(boolean zSingleUse) { //Create a NEW random seed.. MiniData privateseed = MiniData.getRandomData(32); - return createNewKey(privateseed); + return createNewKey(privateseed, zSingleUse); } /** * Create a NEW key */ - public synchronized KeyRow createNewKey(MiniData zPrivateSeed) { + public synchronized KeyRow createNewKey(MiniData zPrivateSeed, boolean zSingleUse) { //Change has occurred mKeyRowChange = true; @@ -188,6 +222,13 @@ public synchronized KeyRow createNewKey(MiniData zPrivateSeed) { SQL_CREATE_PUBLIC_KEY.setString(5, script); SQL_CREATE_PUBLIC_KEY.setString(6, addr.getAddressData().to0xString()); + //Is this a Single Use key - not getaddress + if(zSingleUse) { + SQL_CREATE_PUBLIC_KEY.setInt(7, 1); + }else { + SQL_CREATE_PUBLIC_KEY.setInt(7, 0); + } + //Do it. SQL_CREATE_PUBLIC_KEY.execute(); @@ -203,15 +244,50 @@ public synchronized KeyRow createNewKey(MiniData zPrivateSeed) { /** * Get all relevant Public Keys and Addresses */ - public synchronized ArrayList getAllRelevant() { + public synchronized ArrayList getAllRelevant(boolean zOnlyNonSingleKeys) { //If nop change use the cached version if(!mKeyRowChange) { - return mCachedRelevantKeys; + if(zOnlyNonSingleKeys) { + return mCachedRelevantNonSingleKeys; + } + + return mCachedRelevantAllKeys; } + //And now get all the custom scripts.. + ArrayList customscripts = getAllCustomScripts(); + + //Do both sets.. ArrayList allkeys = new ArrayList<>(); + try { + + //Run the query + ResultSet rs = SQL_GET_ALL_NONSINGLE_RELEVANT.executeQuery(); + + //Could be multiple results + while(rs.next()) { + + //Get the details + String publickey = rs.getString("publickey"); + String address = rs.getString("simpleaddress"); + String script = rs.getString("script"); + String privatekey = rs.getString("privatekey"); + + //Add to our list + allkeys.add(new KeyRow(privatekey, publickey, address, script, true)); + } + + } catch (SQLException e) { + MinimaLogger.log(e); + } + + //Non single keys + mCachedRelevantNonSingleKeys = allkeys; + mCachedRelevantNonSingleKeys.addAll(customscripts); + //Now all the keys.. + allkeys = new ArrayList<>(); try { //Run the query @@ -234,15 +310,19 @@ public synchronized ArrayList getAllRelevant() { MinimaLogger.log(e); } - //And now get all the custom scripts.. - ArrayList customscripts = getAllCustomScripts(); - allkeys.addAll(customscripts); - - //Store for later - mCachedRelevantKeys = allkeys; + //All keys + mCachedRelevantAllKeys = allkeys; + mCachedRelevantAllKeys.addAll(customscripts); + + //Ok - no key change for now.. mKeyRowChange = false; - return allkeys; + //What to return + if(zOnlyNonSingleKeys) { + return mCachedRelevantNonSingleKeys; + } + + return mCachedRelevantAllKeys; } /** diff --git a/src/org/minima/kissvm/Contract.java b/src/org/minima/kissvm/Contract.java index 447ffef51..b660a8400 100644 --- a/src/org/minima/kissvm/Contract.java +++ b/src/org/minima/kissvm/Contract.java @@ -83,7 +83,7 @@ public class Contract { /** * Maximum allowed number of KISSVM instructions */ - public static int MAX_INSTRUCTIONS = 256; + public int MAX_INSTRUCTIONS = 1024; /** * A complete log of the contract execution @@ -200,11 +200,9 @@ public void setGlobals( MiniNumber zBlock, //set the environment setGlobalVariable("@BLOCK", new NumberValue(zBlock)); - setGlobalVariable("@INBLOCK", new NumberValue(zInputBlkCreate)); - setGlobalVariable("@BLOCKDIFF", new NumberValue(zBlock.sub(zInputBlkCreate))); -// setGlobalVariable("@BLKTIME", new NumberValue(zBlock.getTimeMilli())); -// setGlobalVariable("@PREVBLKHASH", new HexValue(zBlock.getParentID())); + setGlobalVariable("@CREATED", new NumberValue(zInputBlkCreate)); + setGlobalVariable("@COINAGE", new NumberValue(zBlock.sub(zInputBlkCreate))); setGlobalVariable("@INPUT", new NumberValue(zInput)); setGlobalVariable("@COINID", new HexValue(cc.getCoinID())); @@ -229,7 +227,7 @@ public Value getGlobal(String zGlobal) throws ExecutionException { } //Will this break monotonic - if(zGlobal.equals("@BLOCK") || zGlobal.equals("@BLOCKDIFF") || zGlobal.equals("@INBLOCK")) { + if(zGlobal.equals("@BLOCK") || zGlobal.equals("@COINAGE") || zGlobal.equals("@CREATED")) { mMonotonic = false; } @@ -380,6 +378,14 @@ public JSONObject getAllVariables() { return variables; } + public void removeVariable(String zName) throws ExecutionException { + mVariables.remove(zName); + } + + public boolean existsVariable(String zName) throws ExecutionException { + return mVariables.containsKey(zName); + } + public Value getVariable(String zName) throws ExecutionException { Value ret = mVariables.get(zName); return ret; @@ -591,7 +597,8 @@ public static String cleanScript(String zScript) { public static void main(String[] zArgs) { - String scr = new String("let (a 1 0xFF ) = 4 + -2 let t = concat( 0x00 0x34 0x45 )"); +// String scr = new String("let (a 1 0xFF ) = 4 + -2 let t = concat( 0x00 0x34 0x45 )"); + String scr = new String("let x = $1"); String clean = Contract.cleanScript(scr); diff --git a/src/org/minima/kissvm/expressions/BooleanExpression.java b/src/org/minima/kissvm/expressions/BooleanExpression.java index e4164eb88..d4f941d6c 100644 --- a/src/org/minima/kissvm/expressions/BooleanExpression.java +++ b/src/org/minima/kissvm/expressions/BooleanExpression.java @@ -61,6 +61,9 @@ public BooleanValue getBooleanValue(Contract zContract) throws ExecutionExceptio public Value getValue(Contract zContract) throws ExecutionException { Value ret = null; + //This action counts as one instruction + zContract.incrementInstructions(); + //Calculate the left and the right side Value lval = mLeft.getValue(zContract); Value rval = mRight.getValue(zContract); @@ -227,9 +230,6 @@ public Value getValue(Contract zContract) throws ExecutionException { default : throw new ExecutionException("UNKNOWN boolean operator : "+mBooleanType); } - - //This action counts as one instruction - zContract.incrementInstructions(); //And trace it.. zContract.traceLog(toString()+" returns:"+ret.toString()); diff --git a/src/org/minima/kissvm/expressions/FunctionExpression.java b/src/org/minima/kissvm/expressions/FunctionExpression.java index 88aa1c15b..1d7b35e7d 100644 --- a/src/org/minima/kissvm/expressions/FunctionExpression.java +++ b/src/org/minima/kissvm/expressions/FunctionExpression.java @@ -26,12 +26,12 @@ public FunctionExpression(MinimaFunction zFunction) { @Override public Value getValue(Contract zContract) throws ExecutionException { - //Get the Value - Value val = mFunction.runFunction(zContract); - //This action counts as one instruction zContract.incrementInstructions(); + //Get the Value + Value val = mFunction.runFunction(zContract); + //And trace it.. zContract.traceLog(toString()+" returns:"+val.toString()); diff --git a/src/org/minima/kissvm/expressions/OperatorExpression.java b/src/org/minima/kissvm/expressions/OperatorExpression.java index 8700d3b23..a2955486a 100644 --- a/src/org/minima/kissvm/expressions/OperatorExpression.java +++ b/src/org/minima/kissvm/expressions/OperatorExpression.java @@ -59,6 +59,9 @@ public OperatorExpression( Expression zLeft, Expression zRight , int zOperator) public Value getValue(Contract zContract) throws ExecutionException { Value ret = null; + //This action counts as one instruction + zContract.incrementInstructions(); + Value lval = mLeft.getValue(zContract); Value rval = mRight.getValue(zContract); @@ -205,9 +208,6 @@ public Value getValue(Contract zContract) throws ExecutionException { default : throw new ExecutionException("UNKNOWN operator"); } - - //This action counts as one instruction - zContract.incrementInstructions(); //And trace it.. zContract.traceLog(toString()+" returns:"+ret.toString()); diff --git a/src/org/minima/kissvm/functions/MinimaFunction.java b/src/org/minima/kissvm/functions/MinimaFunction.java index a620b26ca..36a6302b7 100644 --- a/src/org/minima/kissvm/functions/MinimaFunction.java +++ b/src/org/minima/kissvm/functions/MinimaFunction.java @@ -14,6 +14,7 @@ import org.minima.kissvm.functions.cast.HEX; import org.minima.kissvm.functions.cast.NUMBER; import org.minima.kissvm.functions.cast.STRING; +import org.minima.kissvm.functions.general.FUNCTION; import org.minima.kissvm.functions.general.GET; import org.minima.kissvm.functions.hex.BITCOUNT; import org.minima.kissvm.functions.hex.BITGET; @@ -42,6 +43,7 @@ import org.minima.kissvm.functions.state.PREVSTATE; import org.minima.kissvm.functions.state.SAMESTATE; import org.minima.kissvm.functions.state.STATE; +import org.minima.kissvm.functions.string.CLEAN; import org.minima.kissvm.functions.string.REPLACE; import org.minima.kissvm.functions.string.SUBSTR; import org.minima.kissvm.functions.string.UTF8; @@ -49,10 +51,12 @@ import org.minima.kissvm.functions.txn.input.GETINAMT; import org.minima.kissvm.functions.txn.input.GETINID; import org.minima.kissvm.functions.txn.input.GETINTOK; +import org.minima.kissvm.functions.txn.input.SUMINPUTS; import org.minima.kissvm.functions.txn.input.VERIFYIN; import org.minima.kissvm.functions.txn.output.GETOUTADDR; import org.minima.kissvm.functions.txn.output.GETOUTAMT; import org.minima.kissvm.functions.txn.output.GETOUTTOK; +import org.minima.kissvm.functions.txn.output.SUMOUTPUTS; import org.minima.kissvm.functions.txn.output.VERIFYOUT; import org.minima.kissvm.values.Value; @@ -70,7 +74,8 @@ public abstract class MinimaFunction { new CONCAT(), new LEN(), new REV(),new SUBSET(), new GET(), new ADDRESS(), new BOOL(), new HEX(), new NUMBER(), new STRING(), new ABS(), new CEIL(), new FLOOR(),new MAX(), new MIN(), new DEC(), new INC(), - new SIGDIG(), new POW(), + new SIGDIG(), new POW(), new FUNCTION(), + new SUMINPUTS(),new SUMOUTPUTS(), new CLEAN(), new REPLACE(), new SUBSTR(), new OVERWRITE(), new UTF8(), new KECCAK(), new SHA2(), new SHA3(), new PROOF(), new BITSET(), new BITGET(), new BITCOUNT(), diff --git a/src/org/minima/kissvm/functions/general/FUNCTION.java b/src/org/minima/kissvm/functions/general/FUNCTION.java new file mode 100644 index 000000000..3b9833aff --- /dev/null +++ b/src/org/minima/kissvm/functions/general/FUNCTION.java @@ -0,0 +1,93 @@ +package org.minima.kissvm.functions.general; + +import java.util.List; + +import org.minima.kissvm.Contract; +import org.minima.kissvm.exceptions.ExecutionException; +import org.minima.kissvm.functions.MinimaFunction; +import org.minima.kissvm.statements.StatementBlock; +import org.minima.kissvm.statements.StatementParser; +import org.minima.kissvm.tokens.ScriptToken; +import org.minima.kissvm.tokens.ScriptTokenizer; +import org.minima.kissvm.values.BooleanValue; +import org.minima.kissvm.values.StringValue; +import org.minima.kissvm.values.Value; + +public class FUNCTION extends MinimaFunction{ + + public static String FUNCTION_RETURN = "returnvalue"; + + public FUNCTION() { + super("FUNCTION"); + } + + @Override + public Value runFunction(Contract zContract) throws ExecutionException { + checkMinParamNumber(requiredParams()); + + //get the Script.. + StringValue script = zContract.getStringParam(0, this); + + //Replace all the $ variables.. + String finalfunction = script.toString(); + int params = getAllParameters().size(); + for(int i=1;i tokens = tokz.tokenize(); + + //And now convert to a statement block.. + StatementBlock mBlock = StatementParser.parseTokens(tokens); + + //Now run it.. + mBlock.run(zContract); + + }catch(ExecutionException exc) { + throw exc; + + }catch(Exception exc) { + throw new ExecutionException(exc.toString()); + } + + //Is there a return variable.. + if(zContract.existsVariable(FUNCTION_RETURN)) { + //Get the return vale.. + return zContract.getVariable(FUNCTION_RETURN); + } + + return new BooleanValue(true); + } + + @Override + public boolean isRequiredMinimumParameterNumber() { + return true; + } + + @Override + public int requiredParams() { + return 1; + } + + @Override + public MinimaFunction getNewFunction() { + return new FUNCTION(); + } +} diff --git a/src/org/minima/kissvm/functions/general/GET.java b/src/org/minima/kissvm/functions/general/GET.java index dd897294f..1672b7b98 100644 --- a/src/org/minima/kissvm/functions/general/GET.java +++ b/src/org/minima/kissvm/functions/general/GET.java @@ -17,9 +17,8 @@ public GET() { @Override public Value runFunction(Contract zContract) throws ExecutionException { checkMinParamNumber(requiredParams()); - checkAllParamsType(Value.VALUE_NUMBER, zContract); - //The final Param String to search for + //The full parameter String to search for String ps = ""; //Get all the parameters diff --git a/src/org/minima/kissvm/functions/hex/LEN.java b/src/org/minima/kissvm/functions/hex/LEN.java index 4272cf4b2..6e55d6ec5 100644 --- a/src/org/minima/kissvm/functions/hex/LEN.java +++ b/src/org/minima/kissvm/functions/hex/LEN.java @@ -5,6 +5,7 @@ import org.minima.kissvm.functions.MinimaFunction; import org.minima.kissvm.values.HexValue; import org.minima.kissvm.values.NumberValue; +import org.minima.kissvm.values.StringValue; import org.minima.kissvm.values.Value; public class LEN extends MinimaFunction{ @@ -18,10 +19,24 @@ public Value runFunction(Contract zContract) throws ExecutionException { checkExactParamNumber(requiredParams()); //The Data - HexValue hex = zContract.getHexParam(0, this); - int len = hex.getRawData().length; + Value val = getParameter(0).getValue(zContract); - return new NumberValue(len); + if(val.getValueType() == Value.VALUE_HEX) { + + HexValue hv = (HexValue)val; + int len = hv.getRawData().length; + + return new NumberValue(len); + + }else if(val.getValueType() == Value.VALUE_SCRIPT) { + + StringValue sv = (StringValue)val; + int len = sv.toString().length(); + + return new NumberValue(len); + } + + throw new ExecutionException("LEN requires HEX or STRING param @ "+val.toString()); } @Override diff --git a/src/org/minima/kissvm/functions/txn/input/SUMINPUTS.java b/src/org/minima/kissvm/functions/txn/input/SUMINPUTS.java new file mode 100644 index 000000000..9e0bbacab --- /dev/null +++ b/src/org/minima/kissvm/functions/txn/input/SUMINPUTS.java @@ -0,0 +1,70 @@ +package org.minima.kissvm.functions.txn.input; + +import java.util.ArrayList; + +import org.minima.kissvm.Contract; +import org.minima.kissvm.exceptions.ExecutionException; +import org.minima.kissvm.functions.MinimaFunction; +import org.minima.kissvm.values.NumberValue; +import org.minima.kissvm.values.Value; +import org.minima.objects.Coin; +import org.minima.objects.Token; +import org.minima.objects.Transaction; +import org.minima.objects.base.MiniData; +import org.minima.objects.base.MiniNumber; + +public class SUMINPUTS extends MinimaFunction { + + public SUMINPUTS() { + super("SUMINPUTS"); + } + + @Override + public Value runFunction(Contract zContract) throws ExecutionException { + checkExactParamNumber(requiredParams()); + + //Which Token + MiniData tokenid = zContract.getHexParam(0, this).getMiniData(); + + //Get the Transaction + Transaction trans = zContract.getTransaction(); + + //The Total + MiniNumber total = MiniNumber.ZERO; + + //Cycle through the inputs.. + ArrayList inputs = trans.getAllInputs(); + for(Coin cc : inputs) { + + if(cc.getTokenID().isEqual(tokenid)) { + + if(tokenid.isEqual(Token.TOKENID_MINIMA)) { + + //Plain Minima + total = total.add(cc.getAmount()); + + }else { + + //Get the token.. + Token td = cc.getToken(); + + //Add the scaled amount.. + total = total.add(td.getScaledTokenAmount(cc.getAmount())); + } + } + } + + //Return the Amount + return new NumberValue(total); + } + + @Override + public int requiredParams() { + return 1; + } + + @Override + public MinimaFunction getNewFunction() { + return new SUMINPUTS(); + } +} diff --git a/src/org/minima/kissvm/functions/txn/output/SUMOUTPUTS.java b/src/org/minima/kissvm/functions/txn/output/SUMOUTPUTS.java new file mode 100644 index 000000000..cc427d287 --- /dev/null +++ b/src/org/minima/kissvm/functions/txn/output/SUMOUTPUTS.java @@ -0,0 +1,70 @@ +package org.minima.kissvm.functions.txn.output; + +import java.util.ArrayList; + +import org.minima.kissvm.Contract; +import org.minima.kissvm.exceptions.ExecutionException; +import org.minima.kissvm.functions.MinimaFunction; +import org.minima.kissvm.values.NumberValue; +import org.minima.kissvm.values.Value; +import org.minima.objects.Coin; +import org.minima.objects.Token; +import org.minima.objects.Transaction; +import org.minima.objects.base.MiniData; +import org.minima.objects.base.MiniNumber; + +public class SUMOUTPUTS extends MinimaFunction { + + public SUMOUTPUTS() { + super("SUMOUTPUTS"); + } + + @Override + public Value runFunction(Contract zContract) throws ExecutionException { + checkExactParamNumber(requiredParams()); + + //Which Token + MiniData tokenid = zContract.getHexParam(0, this).getMiniData(); + + //Get the Transaction + Transaction trans = zContract.getTransaction(); + + //The Total + MiniNumber total = MiniNumber.ZERO; + + //Cycle through the inputs.. + ArrayList outputs = trans.getAllOutputs(); + for(Coin cc : outputs) { + + if(cc.getTokenID().isEqual(tokenid)) { + + if(tokenid.isEqual(Token.TOKENID_MINIMA)) { + + //Plain Minima + total = total.add(cc.getAmount()); + + }else { + + //Get the token.. + Token td = cc.getToken(); + + //Add the scaled amount.. + total = total.add(td.getScaledTokenAmount(cc.getAmount())); + } + } + } + + //Return the Amount + return new NumberValue(total); + } + + @Override + public int requiredParams() { + return 1; + } + + @Override + public MinimaFunction getNewFunction() { + return new SUMOUTPUTS(); + } +} diff --git a/src/org/minima/kissvm/statements/commands/EXECstatement.java b/src/org/minima/kissvm/statements/commands/EXECstatement.java index c55d2f52c..5a4478372 100644 --- a/src/org/minima/kissvm/statements/commands/EXECstatement.java +++ b/src/org/minima/kissvm/statements/commands/EXECstatement.java @@ -43,6 +43,9 @@ public void execute(Contract zContract) throws ExecutionException { //Now run it.. mBlock.run(zContract); + + }catch(ExecutionException exc) { + throw exc; }catch(Exception exc) { throw new ExecutionException(exc.toString()); diff --git a/src/org/minima/kissvm/statements/commands/MASTstatement.java b/src/org/minima/kissvm/statements/commands/MASTstatement.java index a16a0d44a..7a16dabf8 100644 --- a/src/org/minima/kissvm/statements/commands/MASTstatement.java +++ b/src/org/minima/kissvm/statements/commands/MASTstatement.java @@ -56,6 +56,9 @@ public void execute(Contract zContract) throws ExecutionException { //Now run it.. mBlock.run(zContract); + }catch(ExecutionException exc) { + throw exc; + } catch (Exception e) { // TODO Auto-generated catch block throw new ExecutionException(e.toString()); diff --git a/src/org/minima/kissvm/tokens/ScriptToken.java b/src/org/minima/kissvm/tokens/ScriptToken.java index 8a1908f24..04808c9f4 100644 --- a/src/org/minima/kissvm/tokens/ScriptToken.java +++ b/src/org/minima/kissvm/tokens/ScriptToken.java @@ -23,6 +23,7 @@ public class ScriptToken { public static final int TOKEN_CLOSEBRACKET = 8; public static final int TOKEN_TRUE = 9; public static final int TOKEN_FALSE = 10; + public static final int TOKEN_FUNCTIIONPARAM = 11; private int mTokenType; private String mToken; @@ -48,6 +49,8 @@ public String getTokenTypeString() { return "FALSE"; case TOKEN_FUNCTIION: return "FUNCTION"; + case TOKEN_FUNCTIIONPARAM: + return "FUNCTIONPARAM"; case TOKEN_VALUE: return "VALUE"; case TOKEN_OPENBRACKET: diff --git a/src/org/minima/kissvm/tokens/ScriptTokenizer.java b/src/org/minima/kissvm/tokens/ScriptTokenizer.java index d0cb2e1df..cd8c31a8a 100644 --- a/src/org/minima/kissvm/tokens/ScriptTokenizer.java +++ b/src/org/minima/kissvm/tokens/ScriptTokenizer.java @@ -191,6 +191,14 @@ public ArrayList tokenize() throws MinimaParseException{ //It's a String tokens.add(new ScriptToken(ScriptToken.TOKEN_VALUE, str)); + //Is it a function param + }else if(nextchar.equals("$")) { + //get the next word.. + String word = getNextWord(); + + //It's a function param + tokens.add(new ScriptToken(ScriptToken.TOKEN_FUNCTIIONPARAM, word)); + }else{ //get the next word.. String word = getNextWord(); @@ -238,6 +246,11 @@ public ArrayList tokenize() throws MinimaParseException{ tokens.add(new ScriptToken(ScriptToken.TOKEN_GLOBAL, uppercase)); }else if(isVariable(lowercase)) { + //Check length + if(lowercase.length() > 32) { + throw new MinimaParseException("MAX Variable length is 32 @ "+mPos+" "+word); + } + //It's a number tokens.add(new ScriptToken(ScriptToken.TOKEN_VARIABLE, lowercase)); diff --git a/src/org/minima/objects/Address.java b/src/org/minima/objects/Address.java index 8569d2f3b..95981cc1f 100644 --- a/src/org/minima/objects/Address.java +++ b/src/org/minima/objects/Address.java @@ -6,7 +6,6 @@ import org.minima.objects.base.MiniData; import org.minima.objects.base.MiniString; -import org.minima.system.params.GlobalParams; import org.minima.utils.BaseConverter; import org.minima.utils.Crypto; import org.minima.utils.Streamable; @@ -37,10 +36,6 @@ public class Address implements Streamable{ public Address() {} public Address(String zScript) { - this(zScript, GlobalParams.MINIMA_DEFAULT_HASH_STRENGTH); - } - - public Address(String zScript, int zBitLength) { //Convert script.. mScript = new MiniString(zScript); @@ -54,19 +49,14 @@ public Address(String zScript, int zBitLength) { public Address(MiniData zAddressData) { mScript = new MiniString(""); mAddressData = zAddressData; - - if(mAddressData.getLength()<20) { - mMinimaAddress = mAddressData.to0xString(); - }else { - mMinimaAddress = makeMinimaAddress(mAddressData); - } + mMinimaAddress = makeMinimaAddress(mAddressData); } public JSONObject toJSON() { JSONObject addr = new JSONObject(); addr.put("script", mScript.toString()); addr.put("hexaddress", mAddressData.toString()); -// addr.put("miniaddress", mMinimaAddress); + addr.put("miniaddress", mMinimaAddress); return addr; } @@ -102,14 +92,9 @@ public void writeDataStream(DataOutputStream zOut) throws IOException { @Override public void readDataStream(DataInputStream zIn) throws IOException { - mAddressData = MiniData.ReadHashFromStream(zIn); - mScript = MiniString.ReadFromStream(zIn); - - if(mAddressData.getLength()<20) { - mMinimaAddress = mAddressData.to0xString(); - }else { - mMinimaAddress = makeMinimaAddress(mAddressData); - } + mAddressData = MiniData.ReadHashFromStream(zIn); + mScript = MiniString.ReadFromStream(zIn); + mMinimaAddress = makeMinimaAddress(mAddressData); } public static Address ReadFromStream(DataInputStream zIn) throws IOException { @@ -120,169 +105,71 @@ public static Address ReadFromStream(DataInputStream zIn) throws IOException { /** * Convert an address into a Minima Checksum Base32 address - * - * @param zAddress - * @return the address */ - public static String makeMinimaAddress(MiniData zAddress) throws ArithmeticException { + public static String makeMinimaAddress(MiniData zAddress){ //The Original data byte[] data = zAddress.getBytes(); //First hash it to add some checksum digits.. - byte[] hash = Crypto.getInstance().hashData(data, 256); - - //Calculate a new length - ONLY certain lengths allowed! - int len = data.length; - int newlen = 0; - - //160 bit - no checksum for 160 bit address.. not expecting people to use it though.. - if(len == 20) { - newlen = 20; - - //192 bit - }else if(len == 24) { - newlen = 25; - - //224 bit - }else if(len == 28) { - newlen = 30; - - //256 bit - }else if(len == 32) { - newlen = 35; - - //288 bit - }else if(len == 36) { - newlen = 40; - - //320 bit - }else if(len == 40) { - newlen = 45; - - //384 bit - }else if(len == 48) { - newlen = 50; - - //416 bit - }else if(len == 52) { - newlen = 55; - - //448 bit - }else if(len == 56) { - newlen = 60; - - //480 bit - }else if(len == 60) { - newlen = 65; - - //512 bit - }else if(len == 64) { - newlen = 70; + byte[] hash = Crypto.getInstance().hashData(data); - }else { - return zAddress.to0xString(); - - //Hmm.. should we through an error ? - //throw new IllegalArgumentException("ERROR - Make Minima Address : not a valid length address!"); - } - - int nbytes = newlen - len; - - //Add the first 4 digits.. - byte[] addr = new byte[len+nbytes]; + //Now create one big byte array - address + first 4 bytes of hash + byte[] tot16 = new byte[data.length + 4]; //Copy the old.. - for(int i=0;i0) { + found = MinimaDB.getDB().getArchive().exists(zGreeting.getChain().get(size-1).to0xString()); } } diff --git a/src/org/minima/objects/Magic.java b/src/org/minima/objects/Magic.java index a5b82b038..e7308f11e 100644 --- a/src/org/minima/objects/Magic.java +++ b/src/org/minima/objects/Magic.java @@ -11,75 +11,102 @@ import org.minima.utils.Streamable; import org.minima.utils.json.JSONObject; +/** + * These Numbers define the capacity of the Minima network + * + * @author spartacusrex + * + */ public class Magic implements Streamable { /** * Used to calculate the weighted averages */ - public static final MiniNumber CALC_WEIGHTED = new MiniNumber(16383); - public static final MiniNumber CALC_TOTAL = new MiniNumber(16384); + private static final MiniNumber CALC_WEIGHTED = new MiniNumber(16383); + private static final MiniNumber CALC_TOTAL = new MiniNumber(16384); /** - * Default starting values.. + * These are HARD limits that can NEVER Change */ - public static final MiniNumber MAX_TXPOW_SIZE = new MiniNumber(32000); - public static final MiniNumber MAX_TXPOW_TXNS = new MiniNumber(100); + private static final MiniNumber MINMAX_TXPOW_SIZE = new MiniNumber(64*1024); + private static final MiniNumber MINMAX_KISSVM_OPERATIONS = new MiniNumber(1024); + private static final MiniNumber MINMAX_TXPOW_TXNS = new MiniNumber(256); /** - * The minimum amount of work for a TxPoW to be allowed across the network + * Minimum acceptable PoW per TxPoW - Also a HARD limit + * + * 0.1 MHash is the minimum.. + */ + public static final MiniNumber MIN_HASHES = new MiniNumber(100000); + public static final BigInteger MIN_TXPOW_VAL = Crypto.MAX_VAL.divide(MIN_HASHES.getAsBigInteger()); + public static final MiniData MIN_TXPOW_WORK = new MiniData(MIN_TXPOW_VAL); + + /** + * Default Maximum size of a TxPoW unit.. Can change + */ + private static final MiniNumber DEFAULT_TXPOW_SIZE = new MiniNumber(64*1024); + + /** + * Default Maximum Number of executed KISSVM Operations */ - public static final BigInteger MEGA_VAL = Crypto.MAX_VAL.divide(new BigInteger("1000")); - public static final MiniData MIN_TXPOW_WORK = new MiniData("0x"+MEGA_VAL.toString(16)); + private static final MiniNumber DEFAULT_KISSVM_OPERATIONS = new MiniNumber(1024); + /** + * Default Maximum number of Txns per block + */ + private static final MiniNumber DEFAULT_TXPOW_TXNS = new MiniNumber(256); + + /** * The Current MAGIC numbers.. based on a weighted average of the chain.. * - * This is ( 9999*the last current values + 1*Desired value ) / 10000 - * + * This is ( 16383*the last current values + 1*Desired value ) / 16384 */ public MiniNumber mCurrentMaxTxPoWSize; + public MiniNumber mCurrentMaxKISSVMOps; public MiniNumber mCurrentMaxTxnPerBlock; public MiniData mCurrentMinTxPoWWork; - /** - * The user votes on what he thinks it should be.. - * - * MUST BE >= x0.5 and <= x2 of the current values. - */ public MiniNumber mDesiredMaxTxPoWSize; + public MiniNumber mDesiredMaxKISSVMOps; public MiniNumber mDesiredMaxTxnPerBlock; public MiniData mDesiredMinTxPoWWork; public Magic() { - mCurrentMaxTxPoWSize = MAX_TXPOW_SIZE; - mCurrentMaxTxnPerBlock = MAX_TXPOW_TXNS; + mCurrentMaxTxPoWSize = DEFAULT_TXPOW_SIZE; + mCurrentMaxKISSVMOps = DEFAULT_KISSVM_OPERATIONS; + mCurrentMaxTxnPerBlock = DEFAULT_TXPOW_TXNS; mCurrentMinTxPoWWork = MIN_TXPOW_WORK; - mDesiredMaxTxPoWSize = MAX_TXPOW_SIZE; - mDesiredMaxTxnPerBlock = MAX_TXPOW_TXNS; + mDesiredMaxTxPoWSize = DEFAULT_TXPOW_SIZE; + mDesiredMaxKISSVMOps = DEFAULT_KISSVM_OPERATIONS; + mDesiredMaxTxnPerBlock = DEFAULT_TXPOW_TXNS; mDesiredMinTxPoWWork = MIN_TXPOW_WORK; } public JSONObject toJSON() { JSONObject magic = new JSONObject(); - magic.put("desiredmaxtxpow", mDesiredMaxTxPoWSize.toString()); + magic.put("currentmaxtxpowsize", mCurrentMaxTxPoWSize.toString()); + magic.put("currentmaxkissvmops", mCurrentMaxKISSVMOps.toString()); + magic.put("currentmaxtxn", mCurrentMaxTxnPerBlock.toString()); + magic.put("currentmintxpowwork", mCurrentMinTxPoWWork.to0xString()); + + magic.put("desiredmaxtxpowsize", mDesiredMaxTxPoWSize.toString()); + magic.put("desiredmaxkissvmops", mDesiredMaxKISSVMOps.toString()); magic.put("desiredmaxtxn", mDesiredMaxTxnPerBlock.toString()); magic.put("desiredmintxpowwork", mDesiredMinTxPoWWork.to0xString()); - magic.put("maxtxpow", mCurrentMaxTxPoWSize.toString()); - magic.put("maxtxn", mCurrentMaxTxnPerBlock.toString()); - magic.put("mintxpowwork", mCurrentMinTxPoWWork.to0xString()); - return magic; } public boolean checkSame(Magic zMagic) { - boolean x = mCurrentMaxTxPoWSize.isEqual(zMagic.mCurrentMaxTxPoWSize); + boolean w = mCurrentMaxTxPoWSize.isEqual(zMagic.mCurrentMaxTxPoWSize); + boolean x = mCurrentMaxKISSVMOps.isEqual(zMagic.mCurrentMaxKISSVMOps); boolean y = mCurrentMaxTxnPerBlock.isEqual(zMagic.mCurrentMaxTxnPerBlock); - boolean w = mCurrentMinTxPoWWork.isEqual(zMagic.mCurrentMinTxPoWWork); + boolean z = mCurrentMinTxPoWWork.isEqual(zMagic.mCurrentMinTxPoWWork); - return x && y && w; + return w && x && y && z; } /** @@ -89,6 +116,10 @@ public MiniNumber getMaxTxPoWSize() { return mCurrentMaxTxPoWSize; } + public MiniNumber getMaxKISSOps() { + return mCurrentMaxKISSVMOps; + } + public MiniNumber getMaxNumTxns() { return mCurrentMaxTxnPerBlock; } @@ -98,25 +129,91 @@ public MiniData getMinTxPowWork() { } /** - * Calculate the current MAX values by taking a heavily weighted average + * Calculate the current MAX values by taking a heavily weighted average + * + * Desired MUST be >= x0.5 and <= x2 + * */ - public void calculateNewCurrent(Magic zParentMagic) { + public Magic calculateNewCurrent() { - // ( 16383*old + new ) / 16384 .. very simple - MiniNumber parent = zParentMagic.getMaxTxPoWSize(); - mCurrentMaxTxPoWSize = parent.mult(CALC_WEIGHTED).add(mDesiredMaxTxPoWSize).div(CALC_TOTAL); + //The New Magic Numbers + Magic ret = new Magic(); + + //TxPoWSize + MiniNumber desired = mDesiredMaxTxPoWSize; + MiniNumber min = mCurrentMaxTxPoWSize.div(MiniNumber.TWO); + MiniNumber max = mCurrentMaxTxPoWSize.mult(MiniNumber.TWO); + if(desired.isLess(min)) { + desired = min; + }else if(desired.isMore(max)) { + desired = max; + + } + + //And finally - this is the minimum limit + if(desired.isLess(MINMAX_TXPOW_SIZE)) { + desired = MINMAX_TXPOW_SIZE; + } + + ret.mCurrentMaxTxPoWSize = mCurrentMaxTxPoWSize.mult(CALC_WEIGHTED).add(desired).div(CALC_TOTAL); + + //KISSVMOpS + desired = mDesiredMaxKISSVMOps; + min = mCurrentMaxKISSVMOps.div(MiniNumber.TWO); + max = mCurrentMaxKISSVMOps.mult(MiniNumber.TWO); + if(desired.isLess(min)) { + desired = min; + }else if(desired.isMore(max)) { + desired = max; + } + + //And finally - this is the minimum limit + if(desired.isLess(MINMAX_KISSVM_OPERATIONS)) { + desired = MINMAX_KISSVM_OPERATIONS; + } + + ret.mCurrentMaxKISSVMOps = mCurrentMaxKISSVMOps.mult(CALC_WEIGHTED).add(desired).div(CALC_TOTAL); + + //Txns per block + desired = mDesiredMaxTxnPerBlock; + min = mCurrentMaxTxnPerBlock.div(MiniNumber.TWO); + max = mCurrentMaxTxnPerBlock.mult(MiniNumber.TWO); + if(desired.isLess(min)) { + desired = min; + }else if(desired.isMore(max)) { + desired = max; + } + + //And finally - this is the minimum limit + if(desired.isLess(MINMAX_TXPOW_TXNS)) { + desired = MINMAX_TXPOW_TXNS; + } + + ret.mCurrentMaxTxnPerBlock = mCurrentMaxTxnPerBlock.mult(CALC_WEIGHTED).add(desired).div(CALC_TOTAL); + + //Work is slightly different as is MiniData + BigInteger two = new BigInteger("2"); + BigInteger oldval = mCurrentMinTxPoWWork.getDataValue(); + BigInteger minval = oldval.divide(two); + BigInteger maxval = oldval.multiply(two); - parent = zParentMagic.getMaxNumTxns(); - mCurrentMaxTxnPerBlock = parent.mult(CALC_WEIGHTED).add(mDesiredMaxTxnPerBlock).div(CALC_TOTAL); - - //Work is slightly differenbt as is MiniData - BigInteger oldval = zParentMagic.getMinTxPowWork().getDataValue(); BigInteger newval = mDesiredMinTxPoWWork.getDataValue(); + if(newval.compareTo(minval)<0) { + newval = minval; + }else if(newval.compareTo(maxval)>0) { + newval = maxval; + } + + //And finally - this is the minimum limit + if(newval.compareTo(MIN_TXPOW_VAL) > 0) { + newval = MIN_TXPOW_VAL; + } //Now do the same calculation.. - BigInteger calc = oldval.multiply(new BigInteger("16383")).add(newval).divide(new BigInteger("16384")); - mCurrentMinTxPoWWork = new MiniData(calc); + BigInteger calc = oldval.multiply(CALC_WEIGHTED.getAsBigInteger()).add(newval).divide(CALC_TOTAL.getAsBigInteger()); + ret.mCurrentMinTxPoWWork = new MiniData(calc); + return ret; } @Override @@ -127,57 +224,29 @@ public String toString() { @Override public void writeDataStream(DataOutputStream zOut) throws IOException { mCurrentMaxTxPoWSize.writeDataStream(zOut); + mCurrentMaxKISSVMOps.writeDataStream(zOut); mCurrentMaxTxnPerBlock.writeDataStream(zOut); - new MiniNumber(128).writeDataStream(zOut); mCurrentMinTxPoWWork.writeDataStream(zOut); mDesiredMaxTxPoWSize.writeDataStream(zOut); + mDesiredMaxKISSVMOps.writeDataStream(zOut); mDesiredMaxTxnPerBlock.writeDataStream(zOut); - new MiniNumber(128).writeDataStream(zOut); mDesiredMinTxPoWWork.writeDataStream(zOut); } @Override public void readDataStream(DataInputStream zIn) throws IOException { - mCurrentMaxTxPoWSize = MiniNumber.ReadFromStream(zIn); - mCurrentMaxTxnPerBlock = MiniNumber.ReadFromStream(zIn); - - //KISS HACK - MiniNumber.ReadFromStream(zIn); - - mCurrentMinTxPoWWork = MiniData.ReadFromStream(zIn); - - mDesiredMaxTxPoWSize = MiniNumber.ReadFromStream(zIn); - mDesiredMaxTxnPerBlock = MiniNumber.ReadFromStream(zIn); + mCurrentMaxTxPoWSize = MiniNumber.ReadFromStream(zIn); + mCurrentMaxKISSVMOps = MiniNumber.ReadFromStream(zIn); + mCurrentMaxTxnPerBlock = MiniNumber.ReadFromStream(zIn); + mCurrentMinTxPoWWork = MiniData.ReadFromStream(zIn); - //KISS HaCK - MiniNumber.ReadFromStream(zIn); - - mDesiredMinTxPoWWork = MiniData.ReadFromStream(zIn); + mDesiredMaxTxPoWSize = MiniNumber.ReadFromStream(zIn); + mDesiredMaxKISSVMOps = MiniNumber.ReadFromStream(zIn); + mDesiredMaxTxnPerBlock = MiniNumber.ReadFromStream(zIn); + mDesiredMinTxPoWWork = MiniData.ReadFromStream(zIn); } -// @Override -// public void writeDataStream(DataOutputStream zOut) throws IOException { -// mCurrentMaxTxPoWSize.writeDataStream(zOut); -// mCurrentMaxTxnPerBlock.writeDataStream(zOut); -// mCurrentMinTxPoWWork.writeDataStream(zOut); -// -// mDesiredMaxTxPoWSize.writeDataStream(zOut); -// mDesiredMaxTxnPerBlock.writeDataStream(zOut); -// mDesiredMinTxPoWWork.writeDataStream(zOut); -// } -// -// @Override -// public void readDataStream(DataInputStream zIn) throws IOException { -// mCurrentMaxTxPoWSize = MiniNumber.ReadFromStream(zIn); -// mCurrentMaxTxnPerBlock = MiniNumber.ReadFromStream(zIn); -// mCurrentMinTxPoWWork = MiniData.ReadFromStream(zIn); -// -// mDesiredMaxTxPoWSize = MiniNumber.ReadFromStream(zIn); -// mDesiredMaxTxnPerBlock = MiniNumber.ReadFromStream(zIn); -// mDesiredMinTxPoWWork = MiniData.ReadFromStream(zIn); -// } - public static Magic ReadFromStream(DataInputStream zIn) throws IOException { Magic mag = new Magic(); mag.readDataStream(zIn); @@ -185,22 +254,25 @@ public static Magic ReadFromStream(DataInputStream zIn) throws IOException { } public static void main(String[] zArgs) { + + MiniNumber desired = new MiniNumber(3000); + + System.out.println("Start:1024 Desired:"+desired); - Magic oldparam = new Magic(); - - Magic newparam = new Magic(); - newparam.mDesiredMaxTxnPerBlock = new MiniNumber(200); - newparam.mDesiredMinTxPoWWork = new MiniData("0x3189374BC6A7EF9DB22D0E5604189374BC6A7EF9DB22D0E5604189374BC6A7"); - - newparam.calculateNewCurrent(oldparam); - System.out.println(newparam.toJSON()); - - for(int i=0;i<16384;i++) { + int days=0; + Magic current = new Magic(); +// current.mCurrentMaxKISSVMOps = new MiniNumber(2000); + for(int i=0;i<1728*50;i++) { + if(i%1000==0) { + current.mDesiredMaxKISSVMOps = desired; + } - newparam.calculateNewCurrent(newparam); + current = current.calculateNewCurrent(); - System.out.println(i+") "+newparam.toJSON()); - } - + if(i%50 == 0) { + days++; + System.out.println(days+") "+current.mCurrentMaxKISSVMOps); + } + } } } diff --git a/src/org/minima/objects/StateVariable.java b/src/org/minima/objects/StateVariable.java index 699a5bb23..ad3aed3b0 100644 --- a/src/org/minima/objects/StateVariable.java +++ b/src/org/minima/objects/StateVariable.java @@ -80,11 +80,11 @@ public StateVariable(int zPort, String zData, boolean zKeepMMR) { mPort = new MiniByte(zPort); //Set the Data - if(zData.startsWith("Mx")) { + if(zData.toLowerCase().startsWith("mx")) { mData = new MiniString(Address.convertMinimaAddress(zData).to0xString()); mType = STATETYPE_HEX; - }else if(zData.startsWith("0x")) { + }else if(zData.toLowerCase().startsWith("0x")) { mData = new MiniString(new MiniData(zData).to0xString()); mType = STATETYPE_HEX; diff --git a/src/org/minima/objects/Token.java b/src/org/minima/objects/Token.java index 9b4e2af0b..5a4b4a6c0 100644 --- a/src/org/minima/objects/Token.java +++ b/src/org/minima/objects/Token.java @@ -49,6 +49,11 @@ public class Token implements Streamable{ */ protected MiniString mTokenScript; + /** + * The Block this Token was created in + */ + protected MiniNumber mTokenCreated; + /** * TokenID created after all the details are set */ @@ -67,12 +72,17 @@ protected Token() {} * @param zName */ public Token(MiniData zCoindID, MiniNumber zScale, MiniNumber zMinimaAmount, MiniString zName, MiniString zTokenScript) { + this(zCoindID, zScale, zMinimaAmount, zName, zTokenScript, MiniNumber.ZERO); + } + + public Token(MiniData zCoindID, MiniNumber zScale, MiniNumber zMinimaAmount, MiniString zName, MiniString zTokenScript, MiniNumber zCreated) { mCoinID = zCoindID; mTokenName = zName; mTokenScale = zScale; mTokenMinimaAmount = zMinimaAmount; mTokenScript = new MiniString(zTokenScript.toString()) ; + mTokenCreated = zCreated; calculateTokenID(); } @@ -123,6 +133,10 @@ public MiniData getCoinID() { return mCoinID; } + public MiniNumber getCreated() { + return mTokenCreated; + } + public MiniData getTokenID() { return mTokenID; } @@ -144,7 +158,8 @@ public JSONObject toJSON() { obj.put("decimals", getDecimalPlaces()); obj.put("script", mTokenScript.toString()); obj.put("totalamount", mTokenMinimaAmount.toString()); - obj.put("scale", mTokenScale ); + obj.put("scale", mTokenScale.toString() ); + obj.put("created", mTokenCreated.toString()); obj.put("tokenid", mTokenID.to0xString()); return obj; @@ -185,6 +200,7 @@ public void writeDataStream(DataOutputStream zOut) throws IOException { mTokenScale.writeDataStream(zOut); mTokenMinimaAmount.writeDataStream(zOut); mTokenName.writeDataStream(zOut); + mTokenCreated.writeDataStream(zOut); } @Override @@ -194,6 +210,7 @@ public void readDataStream(DataInputStream zIn) throws IOException { mTokenScale = MiniNumber.ReadFromStream(zIn); mTokenMinimaAmount = MiniNumber.ReadFromStream(zIn); mTokenName = MiniString.ReadFromStream(zIn); + mTokenCreated = MiniNumber.ReadFromStream(zIn); calculateTokenID(); } diff --git a/src/org/minima/objects/Transaction.java b/src/org/minima/objects/Transaction.java index d80c43e7c..e4cbc9e89 100644 --- a/src/org/minima/objects/Transaction.java +++ b/src/org/minima/objects/Transaction.java @@ -9,7 +9,6 @@ import java.util.Enumeration; import java.util.Hashtable; -import org.minima.objects.base.MiniByte; import org.minima.objects.base.MiniData; import org.minima.objects.base.MiniNumber; import org.minima.utils.Crypto; @@ -34,7 +33,7 @@ public class Transaction implements Streamable { * * MUST be the Hash of the Transaction if this is a Burn Transaction */ - protected MiniData mLinkHash = new MiniData("0x00"); + protected MiniData mLinkHash = MiniData.ZERO_TXPOWID; /** * The Inputs that make up the Transaction @@ -94,6 +93,10 @@ public boolean isCheckedMonotonic() { return mHaveCheckedMonotonic && mIsMonotonic; } + public void clearIsMonotonic() { + mHaveCheckedMonotonic = false; + } + public MiniNumber sumInputs() { MiniNumber tot = MiniNumber.ZERO; for(Coin cc : mInputs) { @@ -138,25 +141,35 @@ public MiniNumber sumOutputs(MiniData zTokenID) { public boolean checkValid(){ //Basics int ins = mInputs.size(); - if(ins<1 || ins>256) { + if(ins<1) { return false; } - int outs = mOutputs.size(); - if(outs>256) { + + //Starters - Check total inputs is less than total outputs + MiniNumber totalin = MiniNumber.ZERO; + MiniNumber totalout = MiniNumber.ZERO; + for(Coin cc : mInputs) { + totalin = totalin.add(cc.getAmount()); + } + for(Coin cc : mOutputs) { + totalout = totalout.add(cc.getAmount()); + } + if(totalout.isMore(totalin)) { + MinimaLogger.log("Transaction error : Inputs LESS than Outputs "+totalin+"/"+totalout); return false; } - //Check that all the inputs and outputs are valid Minima Values 0 - 1,000,000,000 + //Check that all the inputs and outputs are valid Minima Values >0 - 1,000,000,000 for(Coin cc : mInputs) { if(!cc.getAmount().isValidMinimaValue()) { - MinimaLogger.log("Transaction error : Input is invalid Minima Amount"); + MinimaLogger.log("Transaction error : Input is invalid Minima Amount "+cc.getAmount().toString()); return false; } } for(Coin cc : mOutputs) { if(!cc.getAmount().isValidMinimaValue()) { - MinimaLogger.log("Transaction error : Output is invalid Minima Amount"); + MinimaLogger.log("Transaction error : Output is invalid Minima Amount "+cc.getAmount().toString()); return false; } } @@ -217,6 +230,23 @@ public boolean checkValid(){ return true; } + /** + * How much Minima is burnt.. + */ + public MiniNumber getBurn() { + //Starters - Check total inputs is less than total outputs + MiniNumber totalin = MiniNumber.ZERO; + MiniNumber totalout = MiniNumber.ZERO; + for(Coin cc : mInputs) { + totalin = totalin.add(cc.getAmount()); + } + for(Coin cc : mOutputs) { + totalout = totalout.add(cc.getAmount()); + } + + return totalin.sub(totalout); + } + /** * Set a state value from 0-255 to a certain value * MAX 255 VAUES @@ -322,6 +352,10 @@ public MiniData getLinkHash() { return mLinkHash; } + public void setLinkHash(MiniData zLinkHash) { + mLinkHash = zLinkHash; + } + /** * Calculate the TransactionID */ @@ -335,9 +369,14 @@ public MiniData getTransactionID() { /** * Calculate the CoinID of an Output + * + * CoinID is calculated for output coins as the hash of the firstcoin + output num + * + * Always use the MMR value as may be ELTOO 0x01 in some transactions.. + * */ - public MiniData calculateCoinID(int zOutput) { - return Crypto.getInstance().hashObjects(mTransactionID, new MiniByte(zOutput)); + public MiniData calculateCoinID(MiniData zBaseCoinID, int zOutput) { + return Crypto.getInstance().hashObjects(zBaseCoinID, new MiniNumber(zOutput)); } @Override @@ -376,44 +415,15 @@ public JSONObject toJSON() { return ret; } - - /** - * Calculate the output coins with correct CoinID - * @return - */ - public ArrayList getOutputCoinsWithCoinID(){ - ArrayList ret = new ArrayList<>(); - - //Need this to be correct - calculateTransactionID(); - - int output=0; - for(Coin coin : mOutputs) { - - //Create a copy.. - Coin copycoin = coin.deepCopy(); - - //What is the coinid.. - MiniData cid = calculateCoinID(output); - copycoin.resetCoinID(cid); - - //add to our list - ret.add(copycoin); - } - - return ret; - } @Override public void writeDataStream(DataOutputStream zOut) throws IOException { - //Max 255 inputs or outputs MiniNumber ins = new MiniNumber(mInputs.size()); ins.writeDataStream(zOut); for(Coin coin : mInputs) { coin.writeDataStream(zOut); } - //Max 255 inputs or outputs MiniNumber outs = new MiniNumber(mOutputs.size()); outs.writeDataStream(zOut); for(Coin coin : mOutputs) { diff --git a/src/org/minima/objects/TxBlock.java b/src/org/minima/objects/TxBlock.java index e66b21e52..4d8992955 100644 --- a/src/org/minima/objects/TxBlock.java +++ b/src/org/minima/objects/TxBlock.java @@ -12,6 +12,7 @@ import org.minima.database.mmr.MMRProof; import org.minima.objects.base.MiniData; import org.minima.objects.base.MiniNumber; +import org.minima.utils.Crypto; import org.minima.utils.MinimaLogger; import org.minima.utils.Streamable; @@ -86,91 +87,6 @@ private TxPoW getTxpoWFromList(MiniData zTxPoWID, ArrayList zAllTrans) { return null; } -// private void calculateCoins(MMR zPreviousMMR, TxPoW zTxPoW) { -// -// //Needs to be a transaction -// if(zTxPoW.isTransaction()) { -// -// //Get all the input coins -// ArrayList coinspent = zTxPoW.getWitness().getAllCoinProofs(); -// -// //And now get all the proofs pointing to the previous block -// for(CoinProof csp : coinspent) { -// //Get the Coin -// Coin coin = csp.getCoin(); -// -// //Get the ENTRY NUmber.. -// MMREntryNumber entry = coin.getMMREntryNumber(); -// -// //Add this to the MMR - so we can get a proof.. -// zPreviousMMR.updateEntry(entry, csp.getMMRProof(), csp.getMMRData()); -// -// //The Proof - from the previous block -// MMRProof proof = zPreviousMMR.getProofToPeak(entry); -// -// //Construct the CoinProof -// CoinProof cp = new CoinProof(coin, proof); -// -// //Add to the list.. -// mSpentCoins.add(cp); -// } -// -// //The state of this Txn -// ArrayList txnstate = zTxPoW.getTransaction().getCompleteState(); -// -// //Create the state all outputs keep.. -// ArrayList newstate = new ArrayList<>(); -// for(StateVariable sv : txnstate) { -// if(sv.isKeepMMR()) { -// newstate.add(sv); -// } -// } -// -// //All the new coins -// ArrayList outputs = zTxPoW.getTransaction().getAllOutputs(); -// int num=0; -// for(Coin newoutput : outputs) { -// -// //Set the correct state variables -// if(newoutput.storeState()) { -// newoutput.setState(newstate); -// } -// -// //Calculate the Correct CoinID for this coin.. TransactionID already calculated -// MiniData coinid = zTxPoW.getTransaction().calculateCoinID(num); -// -// //Create a new coin with correct coinid -// Coin correctcoin = newoutput.getSameCoinWithCoinID(coinid); -// -// //Is this a create token output.. -// if(newoutput.getTokenID().isEqual(Token.TOKENID_CREATE)) { -// -// //Get the Create token details.. -// Token creator = newoutput.getToken(); -// -// //Get the details.. -// Token newtoken = new Token( coinid, -// creator.getScale(), -// newoutput.getAmount(), -// creator.getName(), -// creator.getTokenScript() ); -// -// //Set it.. -// correctcoin.resetTokenID(newtoken.getTokenID()); -// -// //And set that as the token.. -// correctcoin.setToken(newtoken); -// } -// -// //Add to our list -// mNewCoins.add(correctcoin); -// -// //Next coin down -// num++; -// } -// } -// } - /** * Calculate the MMR for both the main and burn transactions */ @@ -183,7 +99,12 @@ private void calculateCoins(MMR zPreviousMMR, TxPoW zTxPoW) { } private void calculateCoins(MMR zPreviousMMR, Transaction zTransaction, Witness zWitness) { - + + //Could be an empty BURN transaction + if(zTransaction.isEmpty()) { + return; + } + //Get all the input coins ArrayList coinspent = zWitness.getAllCoinProofs(); @@ -219,47 +140,70 @@ private void calculateCoins(MMR zPreviousMMR, Transaction zTransaction, Witness } } - //All the new coins + //All the Outputs.. ArrayList outputs = zTransaction.getAllOutputs(); - int num=0; - for(Coin newoutput : outputs) { - - //Calculate the Correct CoinID for this coin.. TransactionID already calculated - MiniData coinid = zTransaction.calculateCoinID(num); - - //Create a new coin with correct coinid - Coin correctcoin = newoutput.getSameCoinWithCoinID(coinid); - - //Set the correct state variables - if(correctcoin.storeState()) { - correctcoin.setState(newstate); + if(coinspent.size()>0) { + //Get the First Coin in the Txn CoinID.. Genesis Transaction is Different + MiniData basecoinid = null; + if(zPreviousMMR.getBlockTime().isEqual(MiniNumber.ONE)) { + + //Because the first address is different this is always unique + basecoinid = zTransaction.getTransactionID(); + }else { + + //Get the First Coin.. + Coin firstcoin = coinspent.get(0).getCoin(); + + //Hash that coin.. + basecoinid = Crypto.getInstance().hashAllObjects( + firstcoin.getCoinID(), + firstcoin.getAddress(), + firstcoin.getAmount(), + firstcoin.getTokenID()); } - - //Is this a create token output.. - if(newoutput.getTokenID().isEqual(Token.TOKENID_CREATE)) { + + //All the new coins + int num=0; + for(Coin newoutput : outputs) { - //Get the Create token details.. - Token creator = newoutput.getToken(); + //Calculate the Correct CoinID for this coin.. + MiniData coinid = zTransaction.calculateCoinID(basecoinid,num); - //Get the details.. - Token newtoken = new Token( coinid, - creator.getScale(), - newoutput.getAmount(), - creator.getName(), - creator.getTokenScript() ); + //Create a new coin with correct coinid + Coin correctcoin = newoutput.getSameCoinWithCoinID(coinid); - //Set it.. - correctcoin.resetTokenID(newtoken.getTokenID()); + //Set the correct state variables + if(correctcoin.storeState()) { + correctcoin.setState(newstate); + } - //And set that as the token.. - correctcoin.setToken(newtoken); + //Is this a create token output.. + if(newoutput.getTokenID().isEqual(Token.TOKENID_CREATE)) { + + //Get the Create token details.. + Token creator = newoutput.getToken(); + + //Get the details.. + Token newtoken = new Token( coinid, + creator.getScale(), + newoutput.getAmount(), + creator.getName(), + creator.getTokenScript(), + mTxPoW.getBlockNumber()); + + //Set it.. + correctcoin.resetTokenID(newtoken.getTokenID()); + + //And set that as the token.. + correctcoin.setToken(newtoken); + } + + //Add to our list + mNewCoins.add(correctcoin); + + //Next coin down + num++; } - - //Add to our list - mNewCoins.add(correctcoin); - - //Next coin down - num++; } } diff --git a/src/org/minima/objects/TxHeader.java b/src/org/minima/objects/TxHeader.java index 0ea193fcf..c368b07b4 100644 --- a/src/org/minima/objects/TxHeader.java +++ b/src/org/minima/objects/TxHeader.java @@ -18,11 +18,19 @@ public class TxHeader implements Streamable { + public static MiniData TEST_NET = new MiniData("0x00"); + public static MiniData MAIN_NET = new MiniData("0x01"); + /** * The NONCE - the user definable data you cycle through to change the final hash of this TxPow */ public MiniNumber mNonce = new MiniNumber(0); + /** + * The Chain ID - This defines the rules this block was made under, MUST be 0x01.. + */ + public MiniData mChainID = TEST_NET; + /** * Time Milli - needs to be a MiniNumber as is used in Scripts.. */ @@ -83,6 +91,7 @@ public MiniData getBodyHash() { public JSONObject toJSON() { JSONObject txpow = new JSONObject(); + txpow.put("chainid", mChainID.toString()); txpow.put("block", mBlockNumber.toString()); txpow.put("blkdiff", mBlockDifficulty.to0xString()); @@ -142,14 +151,12 @@ public JSONObject toJSON() { @Override public void writeDataStream(DataOutputStream zOut) throws IOException { mNonce.writeDataStream(zOut); + mChainID.writeDataStream(zOut); mTimeMilli.writeDataStream(zOut); mBlockNumber.writeDataStream(zOut); mBlockDifficulty.writeDataStream(zOut); //The Super parents are efficiently encoded in RLE - MiniByte cascnum = new MiniByte(GlobalParams.MINIMA_CASCADE_LEVELS); - cascnum.writeDataStream(zOut); - MiniData sparent = null; int counter = 0; for(int i=0;i=GlobalParams.MINIMA_CASCADE_LEVELS) { + _mSuperBlock = GlobalParams.MINIMA_CASCADE_LEVELS-1; + } + //What size are we.. try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); @@ -518,7 +543,7 @@ public void calculateTXPOWID() { /** * This calculates the Log2 of the Difficulty and TxPoW unit.. */ - public int getSuperLevel(MiniData zBlockDifficulty, MiniData zTxPoWID) { + private int getSuperLevel(MiniData zBlockDifficulty, MiniData zTxPoWID) { //What is the BigInteger quot = zBlockDifficulty.getDataValue().divide(zTxPoWID.getDataValue()); diff --git a/src/org/minima/objects/base/MiniNumber.java b/src/org/minima/objects/base/MiniNumber.java index 1d3947393..3daad207d 100644 --- a/src/org/minima/objects/base/MiniNumber.java +++ b/src/org/minima/objects/base/MiniNumber.java @@ -66,10 +66,12 @@ public class MiniNumber implements Streamable, Comparable { public static final MiniNumber ZERO = new MiniNumber("0"); public static final MiniNumber ONE = new MiniNumber("1"); public static final MiniNumber TWO = new MiniNumber("2"); + public static final MiniNumber THREE = new MiniNumber("3"); public static final MiniNumber FOUR = new MiniNumber("4"); public static final MiniNumber EIGHT = new MiniNumber("8"); public static final MiniNumber TWELVE = new MiniNumber("12"); public static final MiniNumber SIXTEEN = new MiniNumber("16"); + public static final MiniNumber TWENTY = new MiniNumber("20"); public static final MiniNumber THIRTYTWO = new MiniNumber("32"); public static final MiniNumber FIFTY = new MiniNumber("50"); public static final MiniNumber SIXTYFOUR = new MiniNumber("64"); diff --git a/src/org/minima/objects/keys/TreeKey.java b/src/org/minima/objects/keys/TreeKey.java index 82f1f3fce..8205250e2 100644 --- a/src/org/minima/objects/keys/TreeKey.java +++ b/src/org/minima/objects/keys/TreeKey.java @@ -9,8 +9,8 @@ public class TreeKey { - public static final int DEFAULT_KEYSPERLEVEL = 16; - public static final int DEFAULT_LEVELS = 2; + public static final int DEFAULT_KEYSPERLEVEL = 64; + public static final int DEFAULT_LEVELS = 3; public static TreeKey createDefault(MiniData zPrivateSeed) { return new TreeKey(zPrivateSeed, DEFAULT_KEYSPERLEVEL, DEFAULT_LEVELS); @@ -232,7 +232,10 @@ public static void main(String[] zArgs) { int maxsigs = 5; - TreeKey kt = new TreeKey(seed, 2, 3); + long timenow = System.currentTimeMillis(); + TreeKey kt = new TreeKey(seed, 256, 3); + long timediff = System.currentTimeMillis() - timenow; + System.out.println("time "+timediff); //Set the pub key MiniData pk = kt.getPublicKey(); @@ -246,13 +249,20 @@ public static void main(String[] zArgs) { for(int i=0;i txpowmagic.getMaxNumTxns().getAsInt()) { + MinimaLogger.log("Too many transactions in block "+zTxPoW.getBlockTransactions().size()+" "+zTxPoW.getTxPoWID()); + return false; + } //Check all the input coinid are Unique - use the MMR proofs! CoinID could be Eltoo ArrayList allcoinid = new ArrayList<>(); @@ -92,7 +135,7 @@ public static boolean checkTxPoWBlock(TxPoWTreeNode zParentNode, TxPoW zTxPoW, A //Convert to unique Set and check equal size HashSet coinset = new HashSet<>(allcoinid); if(coinset.size() != allcoinid.size()) { - MinimaLogger.log("Invalid TxPoW Block with non unique CoinID "+zTxPoW.getTxPoWID()); + MinimaLogger.log("Invalid TxPoW Block with non unique CoinIDs "+zTxPoW.getTxPoWID()); return false; } @@ -101,7 +144,7 @@ public static boolean checkTxPoWBlock(TxPoWTreeNode zParentNode, TxPoW zTxPoW, A //First check this if(zTxPoW.isTransaction()) { - boolean valid = checkTxPoWSimple(parentMMR, zTxPoW, zTxPoW.getBlockNumber()); + boolean valid = checkTxPoWSimple(parentMMR, zTxPoW, zTxPoW); if(!valid) { return false; } @@ -109,7 +152,7 @@ public static boolean checkTxPoWBlock(TxPoWTreeNode zParentNode, TxPoW zTxPoW, A //Now check all the internal Transactions for(TxPoW txpow : zTransactions) { - boolean valid = checkTxPoWSimple(parentMMR, txpow, zTxPoW.getBlockNumber()); + boolean valid = checkTxPoWSimple(parentMMR, txpow, zTxPoW); if(!valid) { return false; } @@ -137,32 +180,42 @@ public static boolean checkTxPoWBlock(TxPoWTreeNode zParentNode, TxPoW zTxPoW, A } /** - * Once accepted basic and signature checks are no longer needed.. + * Make basic checks of this TxPoW */ - public static boolean checkTxPoWSimple(MMR zTipMMR, TxPoW zTxPoW, MiniNumber zBlock) throws Exception { - - //Check the MMR first - as quicker.. - boolean valid = checkMMR(zTipMMR, zTxPoW); - if(!valid) { + public static boolean checkTxPoWBasic(TxPoW zTxPoW) throws Exception { + + //Check ChainID + if(!zTxPoW.getChainID().isEqual(CURRENT_NETWORK)) { + MinimaLogger.log("Wrong TxPoW ChainID! "+zTxPoW.getChainID()+" "+zTxPoW.getTxPoWID()); return false; } - //Now check the scripts - return checkTxPoWScripts(zTipMMR, zTxPoW, zBlock); - } - - /** - * Make basic checks of this TxPoW - */ - public static boolean checkTxPoWBasic(TxPoW zTxPoW) throws Exception { //Check the Transaction.. boolean valid = checkTxPoWBasic(zTxPoW.getTxPoWID(), zTxPoW.getTransaction(), zTxPoW.getWitness()); if(!valid) { return false; } - //Check the Burn Transaction.. - return checkTxPoWBasic(zTxPoW.getTxPoWID(), zTxPoW.getBurnTransaction(), zTxPoW.getBurnWitness()); + //Check the Link Hash + if(!zTxPoW.getTransaction().getLinkHash().isEqual(MiniData.ZERO_TXPOWID)) { + MinimaLogger.log("Invalid LinkHash for Transaction ( NOT 0x00 ) "+zTxPoW.getTxPoWID()); + return false; + } + + //Is there a BURN transaction.. + if(!zTxPoW.getBurnTransaction().isEmpty()) { + + //Check the Link Hash + if(!zTxPoW.getBurnTransaction().getLinkHash().isEqual(zTxPoW.getTransaction().getTransactionID())) { + MinimaLogger.log("Invalid LinkHash for Burn Transaction "+zTxPoW.getTxPoWID()); + return false; + } + + //Check the Burn Transaction.. + return checkTxPoWBasic(zTxPoW.getTxPoWID(), zTxPoW.getBurnTransaction(), zTxPoW.getBurnWitness()); + } + + return true; } private static boolean checkTxPoWBasic(String zTxPoWID, Transaction zTransaction, Witness zWitness) throws Exception { @@ -181,12 +234,18 @@ private static boolean checkTxPoWBasic(String zTxPoWID, Transaction zTransaction ArrayList inputs = zTransaction.getAllInputs(); int ins = inputs.size(); + //MUST be at least 1 input.. + if(ins==0) { + MinimaLogger.log("Transaction MUST have at least 1 input @ "+zTxPoWID); + return false; + } + //Get all the coin proofs.. ArrayList mmrproofs = zWitness.getAllCoinProofs(); //Check we have the correct amount.. if(ins != mmrproofs.size()) { - MinimaLogger.log("MISSING MMR Proofs Inputs:"+ins+" MMRProofs:"+mmrproofs.size()+" @ "+zTxPoWID); + MinimaLogger.log("Wrong Number of MMR Proofs Inputs:"+ins+" MMRProofs:"+mmrproofs.size()+" @ "+zTxPoWID); return false; } @@ -210,16 +269,6 @@ private static boolean checkTxPoWBasic(String zTxPoWID, Transaction zTransaction } allcoinsused.add(coinid); - //Check tokenid is correct - if(!cproof.getCoin().getTokenID().isEqual(Token.TOKENID_MINIMA)) { - - //Check the token is correct - if(!cproof.getCoin().getTokenID().isEqual(cproof.getCoin().getToken().getTokenID())) { - MinimaLogger.log("TokenID in input "+i+" doesn't match token "+zTxPoWID); - return false; - } - } - //Check the CoinProof details and Coin details Match boolean amount = input.getAmount().isEqual(cproof.getCoin().getAmount()); boolean address = input.getAddress().isEqual(cproof.getCoin().getAddress()); @@ -230,19 +279,27 @@ private static boolean checkTxPoWBasic(String zTxPoWID, Transaction zTransaction } //Check the CoinProof and Coin CoinID in the Transaction Match - if(input.getCoinID().isEqual(Coin.COINID_ELTOO)) { + if(!input.getCoinID().isEqual(Coin.COINID_ELTOO)) { - //Check is a floating input.. set when the coin was created! - if(!cproof.getCoin().isFloating()) { - MinimaLogger.log("ELTOO input "+i+" isn't floating "+zTxPoWID); + //Check the same CoinID + if(!input.getCoinID().isEqual(cproof.getCoin().getCoinID())) { + MinimaLogger.log("CoinID input "+i+" doesn't match proof "+zTxPoWID); return false; } + } + + //Check token is correct + if(!input.getTokenID().isEqual(Token.TOKENID_MINIMA)) { - }else { + //Check the token is correct - in the coin + if(!input.getTokenID().isEqual(input.getToken().getTokenID())) { + MinimaLogger.log("TokenID in Coin input "+i+" doesn't match token "+zTxPoWID); + return false; + } - //Check the same CoinID - if(!input.getCoinID().isEqual(cproof.getCoin().getCoinID())) { - MinimaLogger.log("CoinID input "+i+" doesn't match proof "+zTxPoWID); + //Check the token is correct - in the MMR + if(!cproof.getCoin().getTokenID().isEqual(cproof.getCoin().getToken().getTokenID())) { + MinimaLogger.log("TokenID in MMR Proof input "+i+" doesn't match token "+zTxPoWID); return false; } } @@ -264,10 +321,38 @@ private static boolean checkTxPoWBasic(String zTxPoWID, Transaction zTransaction return true; } + /** + * Once accepted basic and signature checks are no longer needed.. + */ + public static boolean checkTxPoWSimple(MMR zTipMMR, TxPoW zTxPoW, TxPoW zBlock) throws Exception { + + //Check TxPoW is required Minimum.. + if(zTxPoW.getTxnDifficulty().isMore(zBlock.getMagic().getMinTxPowWork())) { + MinimaLogger.log("TxPoW difficulty too low.. "+zTxPoW.getTxPoWID()); + return false; + } + + //Check Size is acceptable.. + long size = zTxPoW.getSizeinBytesWithoutBlockTxns(); + if(size > zBlock.getMagic().getMaxTxPoWSize().getAsLong()) { + MinimaLogger.log("TxPoW size too large.. "+size+" "+zTxPoW.getTxPoWID()); + return false; + } + + //Check the MMR first - as quicker.. + boolean valid = checkMMR(zTipMMR, zTxPoW); + if(!valid) { + return false; + } + + //Now check the scripts + return checkTxPoWScripts(zTipMMR, zTxPoW, zBlock); + } + /** * Check the Scripts of a transaction */ - public static boolean checkTxPoWScripts(MMR zTipMMR, TxPoW zTxPoW, MiniNumber zBlock) throws Exception { + public static boolean checkTxPoWScripts(MMR zTipMMR, TxPoW zTxPoW, TxPoW zBlock) throws Exception { //Check the Transaction.. boolean valid = checkTxPoWScripts(zTipMMR, zTxPoW.getTransaction(), zTxPoW.getWitness(), zBlock); @@ -279,7 +364,7 @@ public static boolean checkTxPoWScripts(MMR zTipMMR, TxPoW zTxPoW, MiniNumber zB return checkTxPoWScripts(zTipMMR, zTxPoW.getBurnTransaction(), zTxPoW.getBurnWitness(), zBlock); } - private static boolean checkTxPoWScripts(MMR zTipMMR, Transaction zTransaction, Witness zWitness, MiniNumber zBlock) throws Exception { + private static boolean checkTxPoWScripts(MMR zTipMMR, Transaction zTransaction, Witness zWitness, TxPoW zBlock) throws Exception { //Do we even need to check this! if(zTransaction.isCheckedMonotonic()) { @@ -297,6 +382,9 @@ private static boolean checkTxPoWScripts(MMR zTipMMR, Transaction zTransaction, return true; } + //Max KISSVM Ops + int maxops = zBlock.getMagic().getMaxKISSOps().getAsInt(); + //Get the coin proofs ArrayList mmrproofs = zWitness.getAllCoinProofs(); int ins = mmrproofs.size(); @@ -318,7 +406,8 @@ private static boolean checkTxPoWScripts(MMR zTipMMR, Transaction zTransaction, zTransaction, cproof.getCoin().getState()); - contract.setGlobals(zBlock, zTransaction, i, cproof.getCoin().getBlockCreated(), script); + contract.setMaxInstructions(maxops); + contract.setGlobals(zBlock.getBlockNumber(), zTransaction, i, cproof.getCoin().getBlockCreated(), script); contract.run(); //Monotonic - no @BLKNUM references.. @@ -350,7 +439,8 @@ private static boolean checkTxPoWScripts(MMR zTipMMR, Transaction zTransaction, zTransaction, cproof.getCoin().getState()); - tokcontract.setGlobals(zBlock, zTransaction, i, cproof.getCoin().getBlockCreated(), tokscript); + tokcontract.setMaxInstructions(maxops); + tokcontract.setGlobals(zBlock.getBlockNumber(), zTransaction, i, cproof.getCoin().getBlockCreated(), tokscript); tokcontract.run(); if(!tokcontract.isMonotonic()) { @@ -477,18 +567,70 @@ public static boolean checkMemPoolCoins(TxPoW zTxPoW) { } /** - * Check the Difficulty of one block with another.. + * Check that all the Super Parent nodes are correct */ - public static double checkDifficulty(MiniData zTip, MiniData zBlock) { + public static boolean checkParents(TxPoWTreeNode zTip, TxPoW zBlock) { - BigInteger tip = zTip.getDataValue(); - BigInteger block = zBlock.getDataValue(); + //Cycle back through the chain.. + int blocksup = 0; + TxPoWTreeNode current = zTip; + while(current != null) { + + //Get the TxPoW + TxPoW txpow = current.getTxPoW(); + MiniData txdata = txpow.getTxPoWIDData(); + int superlevel = txpow.getSuperLevel(); + + //Is it more than or equal to current required.. + while(superlevel>=blocksup) { + + //The current super parent of the block + MiniData superparent = zBlock.getSuperParent(blocksup); + + //Make sure is valid.. + if(!superparent.isEqual(txdata)) { + return false; + } + + blocksup++; + } + + current = current.getParent(); + } - BigDecimal tipdec = new BigDecimal(tip); - BigDecimal blockdec = new BigDecimal(block); + //Now go through the cascade + CascadeNode cnode = MinimaDB.getDB().getCascade().getTip(); + while(cnode != null) { + + //Get the TxPoW + TxPoW txpow = cnode.getTxPoW(); + MiniData txdata = txpow.getTxPoWIDData(); + int superlevel = txpow.getSuperLevel(); + + //Is it more than or equal to current required.. + while(superlevel>=blocksup) { + + //The current super parent of the block + MiniData superparent = zBlock.getSuperParent(blocksup); + + //Make sure is valid.. + if(!superparent.isEqual(txdata)) { + return false; + } + + blocksup++; + } + + cnode = cnode.getParent(); + } - BigDecimal div = tipdec.divide(blockdec, MathContext.DECIMAL32); + //Check that the remaining all point to 0x00 + for(int i=blocksup;i0) { - newdifficulty = MIN_TXPOW_VAL; - } + //Warn them.. + MinimaLogger.log("WARNING : Your Hashrate is lower than the current Minimum allowed by the network"); - if(newdifficulty.compareTo(BigInteger.ZERO)<0) { - MinimaLogger.log("SERIOUS ERROR : NEGATIVE DIFFICULTY!"); - MinimaLogger.log("speed : "+speed); - MinimaLogger.log("speedratio : "+speedratio); - MinimaLogger.log("newdifficulty :"+newdifficulty.toString()); - } +// //Add 10%.. to give yourself some space +// BigDecimal hashes = txpowmagic.getMinTxPowWork().getDataValueDecimal(); +// hashes = hashes.divide(new BigDecimal("1.1"), MathContext.DECIMAL128); +// minhash = new MiniData(hashes.toBigInteger()); - txpow.setBlockDifficulty(new MiniData(newdifficulty)); + //This could be too low if the Hash value is going up.. + minhash = txpowmagic.getMinTxPowWork(); } - + txpow.setTxDifficulty(minhash); //And add the current mempool txpow.. ArrayList mempool = MinimaDB.getDB().getTxPoWDB().getAllUnusedTxns(); + //Order the mempool txns by BURN.. + Collections.sort(mempool, new Comparator() { + @Override + public int compare(TxPoW o1, TxPoW o2) { + return o2.getBurn().compareTo(o1.getBurn()); + } + }); + //The final TxPoW transactions put in this TxPoW ArrayList chosentxns = new ArrayList<>(); - - //Order the mempool by BURN.. - //.. - + //A list of the added coins ArrayList addedcoins = new ArrayList<>(); //Add the main transaction inputs.. - ArrayList inputcoins = txpow.getTransaction().getAllInputs(); + ArrayList inputcoins = zTransaction.getAllInputs(); for(Coin cc : inputcoins) { addedcoins.add(cc.getCoinID().to0xString()); } + //Burn Coins + if(zBurnTransaction != null) { + inputcoins = zBurnTransaction.getAllInputs(); + for(Coin cc : inputcoins) { + addedcoins.add(cc.getCoinID().to0xString()); + } + } + //Check them all.. int totaladded = 0; for(TxPoW memtxp : mempool) { + //Is it a transaction + if(!memtxp.isTransaction()) { + continue; + } + + //Start off assuming it's valid + boolean valid = true; try { //Check CoinIDs not added already.. @@ -148,8 +193,17 @@ public static TxPoW generateTxPoW(Transaction zTransaction, Witness zWitness) { } } + //Check against the Magic Numbers + if(memtxp.getSizeinBytesWithoutBlockTxns() > txpowmagic.getMaxTxPoWSize().getAsLong()) { + MinimaLogger.log("Mempool txn too big.. "+memtxp.getTxPoWID()); + valid = false; + }else if(memtxp.getTxnDifficulty().isMore(txpowmagic.getMinTxPowWork())) { + MinimaLogger.log("Mempool txn TxPoW too low.. "+memtxp.getTxPoWID()); + valid = false; + } + //Check if Valid! - if(TxPoWChecker.checkTxPoWSimple(tip.getMMR(), memtxp, txpow.getBlockNumber())) { + if(valid && TxPoWChecker.checkTxPoWSimple(tip.getMMR(), memtxp, txpow)) { //Add to our list chosentxns.add(memtxp); @@ -163,21 +217,23 @@ public static TxPoW generateTxPoW(Transaction zTransaction, Witness zWitness) { ArrayList memtxpinputcoins = memtxp.getTransaction().getAllInputs(); for(Coin cc : memtxpinputcoins) { addedcoins.add(cc.getCoinID().to0xString()); - } - - }else { - - //Invalid TxPoW - remove from mempool - MinimaLogger.log("Invalid TxPoW in mempool.. removing.. "+memtxp.getTxPoWID()); - MinimaDB.getDB().getTxPoWDB().removeMemPoolTxPoW(memtxp.getTxPoWID()); + } } }catch(Exception exc) { - MinimaLogger.log("ERROR Checking TxPoW "+memtxp.getTxPoWID()); + MinimaLogger.log("ERROR Checking TxPoW "+memtxp.getTxPoWID()+" "+exc.toString()); + valid = false; + } + + //Was it valid + if(!valid) { + //Invalid TxPoW - remove from mempool + MinimaLogger.log("Invalid TxPoW in mempool.. removing.. "+memtxp.getTxPoWID()); + MinimaDB.getDB().getTxPoWDB().removeMemPoolTxPoW(memtxp.getTxPoWID()); } - //Max allowed.. 1 txn/s - for now.. - if(totaladded > 50) { + //Max allowed.. + if(totaladded >= txpowmagic.getMaxNumTxns().getAsInt()) { break; } } @@ -197,18 +253,80 @@ public static TxPoW generateTxPoW(Transaction zTransaction, Witness zWitness) { return txpow; } + public static MiniData getBlockDifficulty(TxPoWTreeNode zParent) { + + //Are we just starting out.. first 8 blocks are minimum difficulty + if(zParent.getBlockNumber().isLess(MiniNumber.EIGHT)) { + return Magic.MIN_TXPOW_WORK; + } + + //Start from the parent.. + TxPoWTreeNode startblock = zParent; + + //Where to.. + TxPoWTreeNode endblock = zParent.getParent(GlobalParams.MINIMA_BLOCKS_SPEED_CALC.getAsInt()); + + //Now use the Median Times.. + startblock = getMedianTimeBlock(startblock, GlobalParams.MEDIAN_BLOCK_CALC); + endblock = getMedianTimeBlock(endblock, GlobalParams.MEDIAN_BLOCK_CALC); + MiniNumber blockdiff = startblock.getBlockNumber().sub(endblock.getBlockNumber()); + + //In case of serious time error + MiniNumber timediff = startblock.getTxPoW().getTimeMilli().sub(endblock.getTxPoW().getTimeMilli()); + if(timediff.isLessEqual(MiniNumber.ZERO)) { + //This should not happen.. + MinimaLogger.log("SERIOUS TIME ERROR @ "+zParent.getBlockNumber()+" Using latest block diff.."); + MinimaLogger.log("StartBlock @ "+startblock.getBlockNumber()+" "+new Date(startblock.getTxPoW().getTimeMilli().getAsLong())); + MinimaLogger.log("EndBlock @ "+endblock.getBlockNumber()+" "+new Date(endblock.getTxPoW().getTimeMilli().getAsLong())); + + //Return the LATEST value.. + return zParent.getTxBlock().getTxPoW().getBlockDifficulty(); + } + + //Get current speed + MiniNumber speed = getChainSpeed(startblock, blockdiff); + MiniNumber speedratio = GlobalParams.MINIMA_BLOCK_SPEED.div(speed); + + //Get average difficulty over that period + BigInteger averagedifficulty = getAverageDifficulty(startblock, blockdiff); + BigDecimal averagedifficultydec = new BigDecimal(averagedifficulty); + + //Recalculate.. + BigDecimal newdifficultydec = averagedifficultydec.multiply(speedratio.getAsBigDecimal()); + BigInteger newdifficulty = newdifficultydec.toBigInteger(); + MiniData newdiff = new MiniData(newdifficulty); + + //Check harder than the absolute minimum + if(newdiff.isMore(Magic.MIN_TXPOW_WORK)) { +// MinimaLogger.log("Block Diff so low had to use the Mininum allowed"); + newdiff = Magic.MIN_TXPOW_WORK; + } + + //Check within bounds.. + BigDecimal lastdiffdec = new BigDecimal(zParent.getTxPoW().getBlockDifficulty().getDataValue()); + BigDecimal newdiffdec = new BigDecimal(newdiff.getDataValue()); + BigDecimal blockdiffratio = lastdiffdec.divide(newdiffdec, MathContext.DECIMAL32); + + if(blockdiffratio.compareTo(MAX_BOUND_DIFFICULTY)>0) { + MinimaLogger.log("Increased difficulty change greater than allowed @ "+startblock.getBlockNumber()+" ( "+blockdiffratio+ " ).. Using Bounds"); + + BigDecimal bound = lastdiffdec.divide(MAX_BOUND_DIFFICULTY, MathContext.DECIMAL32); + newdiff = new MiniData(bound.toBigInteger()); + + }else if(blockdiffratio.compareTo(MIN_BOUND_DIFFICULTY)<0) { + MinimaLogger.log("Decreased difficulty change greater than allowed @ "+startblock.getBlockNumber()+" ( "+blockdiffratio+" ).. Using Bounds"); + + BigDecimal bound = lastdiffdec.divide(MIN_BOUND_DIFFICULTY, MathContext.DECIMAL32); + newdiff = new MiniData(bound.toBigInteger()); + } + + return newdiff; + } public static MiniNumber getChainSpeed(TxPoWTreeNode zTopBlock, MiniNumber zBlocksBack) { - //Which block are we looking for.. - MiniNumber block = zTopBlock.getTxPoW().getBlockNumber().sub(zBlocksBack); - - //Get the past block - initially there may be less than that available - TxPoWTreeNode pastblock = zTopBlock.getPastNode(block); - if(pastblock == null) { - //too soon.. - MinimaLogger.log("SPEED TOO SOON "+zTopBlock.getTxPoW().getBlockNumber()+" "+zBlocksBack); - return MiniNumber.ONE; - } + + //Get the past block + TxPoWTreeNode pastblock = zTopBlock.getParent(zBlocksBack.getAsInt()); MiniNumber blockpast = pastblock.getTxPoW().getBlockNumber(); MiniNumber timepast = pastblock.getTxPoW().getTimeMilli(); @@ -222,14 +340,6 @@ public static MiniNumber getChainSpeed(TxPoWTreeNode zTopBlock, MiniNumber zBloc MiniNumber speedmilli = blockdiff.div(timediff); MiniNumber speedsecs = speedmilli.mult(MiniNumber.THOUSAND); - if(speedsecs.isLessEqual(MiniNumber.ZERO)) { - MinimaLogger.log("SERIOUS ERROR NEGATIVE SPEED AS PAST BLOCK AHEAD OF CURRENT TIME!"); - MinimaLogger.log(blockpast+" "+new Date(timepast.getAsLong()).toString()+" "+blocknow+" "+new Date(timenow.getAsLong()).toString()); - MinimaLogger.log("PAST : "+blockpast+" "+pastblock.getTxPoW().getTxPoWID()+" "+timepast.getAsLong()); - MinimaLogger.log("CURRENT : "+blocknow+" "+zTopBlock.getTxPoW().getTxPoWID()+" "+timenow.getAsLong()); - return GlobalParams.MINIMA_BLOCK_SPEED; - } - return speedsecs; } @@ -258,44 +368,104 @@ private static BigInteger getAverageDifficulty(TxPoWTreeNode zTopBlock, MiniNumb } /** - * Get the Median time of the last 128 blocks.. + * Get the Median Block based on milli time.. */ - public static MiniNumber getMedianTime(TxPoWTreeNode zTopBlock) { + public static TxPoWTreeNode getMedianTimeBlock(TxPoWTreeNode zStartBlock, int zBlocksBack) { - //Create a list of times.. - ArrayList alltimes = new ArrayList<>(); + //The block we start checking from + TxPoWTreeNode current = zStartBlock; + + //Create a list of blocks.. + ArrayList allblocks = new ArrayList<>(); - TxPoWTreeNode current = zTopBlock; int counter=0; - while(counter<128 && current!=null) { + while(counter() { + //Now sort them.. by time milli + Collections.sort(allblocks, new Comparator() { @Override - public int compare(MiniNumber o1, MiniNumber o2) { - return o1.compareTo(o2); + public int compare(TxPoWTreeNode o1, TxPoWTreeNode o2) { + return o1.getTxPoW().getTimeMilli().compareTo(o2.getTxPoW().getTimeMilli()); } }); //Now pick the middle one - int size = alltimes.size(); + int middle = allblocks.size()/2; - //Middle.. - MiniNumber median = alltimes.get(size/2); + //Return the middle one! + return allblocks.get(middle); + } + + public static void precomputeTransactionCoinID(Transaction zTransaction) { -// String timenow = new Date(zTopBlock.getTxPoW().getTimeMilli().getAsLong()).toString(); -// String timemedian = new Date(median.getAsLong()).toString(); -// MinimaLogger.log("MEDIAN TIME @ "+timenow+" MEDIAN:"+timemedian+" "+counter); + //Get the inputs.. + ArrayList inputs = zTransaction.getAllInputs(); - //Return the middle one! - return median; + //Are there any.. + if(inputs.size() == 0) { + return; + } + + //Get the first coin.. + Coin firstcoin = inputs.get(0); + + //Is it an ELTOO input + boolean eltoo = false; + if(firstcoin.getCoinID().isEqual(Coin.COINID_ELTOO)) { + eltoo = true; + } + + //The base modifier + MiniData basecoinid = Crypto.getInstance().hashAllObjects( + firstcoin.getCoinID(), + firstcoin.getAddress(), + firstcoin.getAmount(), + firstcoin.getTokenID()); + + //Now cycle.. + ArrayList outputs = zTransaction.getAllOutputs(); + int num=0; + for(Coin output : outputs) { + + //Calculate the CoinID.. + if(eltoo) { + + //Normal + output.resetCoinID(Coin.COINID_OUTPUT); + + }else { + + //The CoinID + MiniData coinid = zTransaction.calculateCoinID(basecoinid, num); + output.resetCoinID(coinid); + } + + num++; + } + } + + public static void main(String[] zArgs) { + + ArrayList nums = new ArrayList<>(); + nums.add(MiniNumber.ZERO); + nums.add(MiniNumber.ONE); + nums.add(MiniNumber.TWO); + + Collections.sort(nums, new Comparator() { + @Override + public int compare(MiniNumber o1, MiniNumber o2) { + return o2.compareTo(o1); + } + }); + + System.out.println(nums.toString()); } } diff --git a/src/org/minima/system/brains/TxPoWMiner.java b/src/org/minima/system/brains/TxPoWMiner.java index b1126bb70..d133de114 100644 --- a/src/org/minima/system/brains/TxPoWMiner.java +++ b/src/org/minima/system/brains/TxPoWMiner.java @@ -31,7 +31,6 @@ public class TxPoWMiner extends MessageProcessor { */ ArrayList mMiningCoins; - public TxPoWMiner() { super("MINER"); @@ -61,9 +60,6 @@ protected void processMessage(Message zMessage) throws Exception { //Set the nonce.. we make it a large size in bytes then edit those - no reserialisation txpow.setNonce(START_NONCE_BYTES); - //Set the Time.. - txpow.setTimeMilli(new MiniNumber(System.currentTimeMillis())); - //Post a message.. Mining Started Message mining = new Message(Main.MAIN_MINING); mining.addBoolean("starting", true); @@ -189,4 +185,25 @@ public boolean checkForMiningCoin(String zCoinID) { return mMiningCoins.contains(zCoinID); } + /** + * Calculate the Hash rate of this node... + */ + public static MiniNumber calculateHashRate() { + + MiniNumber hashes = MiniNumber.MILLION; + int ihashes = hashes.getAsInt(); + + long timestart = System.currentTimeMillis(); + MiniData data = MiniData.getRandomData(32); + for(int i=0;i blocks = ibd.getTxBlocks(); for(TxBlock block : blocks) { //Process it.. - processSyncBlock(block); + try { + //What is the time diff + MiniNumber timediff = timenow.sub(block.getTxPoW().getTimeMilli()); + + //Check if this sync block is too near the current time.. or if we have no blocks yet + if((treelen+additions) 1000) { + + //recalculate the Tree.. + recalculateTree(); + + //Reset these + treelen = txptree.getHeaviestBranchLength(); + additions = 0; + } + + }else { + MinimaLogger.log("TxBlock too close to real time.. skipping.. @ "+block.getTxPoW().getBlockNumber()); + break; + } + + }catch(Exception exc) { + MinimaLogger.log(exc.toString()); + + //Something funny going on.. disconnect + Main.getInstance().getNIOManager().disconnect(uid); + + break; + } } //And now recalculate tree diff --git a/src/org/minima/system/brains/TxPoWSearcher.java b/src/org/minima/system/brains/TxPoWSearcher.java index f8121fa47..ab72ee8cf 100644 --- a/src/org/minima/system/brains/TxPoWSearcher.java +++ b/src/org/minima/system/brains/TxPoWSearcher.java @@ -156,7 +156,7 @@ public static ArrayList searchCoins( TxPoWTreeNode zStartNode, boolean zRe Wallet wallet = MinimaDB.getDB().getWallet(); //Get all the keys - ArrayList keys = wallet.getAllRelevant(); + ArrayList keys = wallet.getAllRelevant(false); //Now cycle through the coins for(Coin cc : coinentry) { diff --git a/src/org/minima/system/commands/Command.java b/src/org/minima/system/commands/Command.java index 24389a95a..04f96a003 100644 --- a/src/org/minima/system/commands/Command.java +++ b/src/org/minima/system/commands/Command.java @@ -5,11 +5,13 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.minima.objects.Address; import org.minima.objects.base.MiniData; import org.minima.objects.base.MiniNumber; import org.minima.system.commands.base.automine; import org.minima.system.commands.base.backup; import org.minima.system.commands.base.balance; +import org.minima.system.commands.base.burn; import org.minima.system.commands.base.coinexport; import org.minima.system.commands.base.coinimport; import org.minima.system.commands.base.cointrack; @@ -50,6 +52,7 @@ import org.minima.system.commands.search.txpow; import org.minima.system.commands.signatures.sign; import org.minima.system.commands.signatures.verify; +import org.minima.system.commands.txn.txnbasics; import org.minima.system.commands.txn.txncheck; import org.minima.system.commands.txn.txnclear; import org.minima.system.commands.txn.txncreate; @@ -76,13 +79,12 @@ public abstract class Command { new message(), new trace(), new help(), new printtree(), new automine(), new printmmr(), new rpc(), new send(), new balance(), new tokencreate(), new tokens(),new getaddress(), new newaddress(), new debugflag(), new incentivecash(), new sshtunnel(), new webhooks(), + + new sql(),new file(), new backup(), new restore(), new test(), + new runscript(), new tutorial(),new keys(),new scripts(),new burn(), - new file(), new sql(), - - new runscript(), new tutorial(),new keys(),new scripts(), - - new txncreate(), new txninput(),new txnlist(), new txnclear(), + new txnbasics(),new txncreate(), new txninput(),new txnlist(), new txnclear(), new txnoutput(),new txnstate(),new txnsign(),new txnpost(),new txndelete(), new txnexport(),new txnimport(),new txncheck(), new txnscript(), @@ -176,13 +178,18 @@ public MiniNumber getNumberParam(String zParamName) throws CommandException { return new MiniNumber(num); } + public MiniNumber getNumberParam(String zParamName, MiniNumber zDefault) throws CommandException { + if(existsParam(zParamName)) { + return getNumberParam(zParamName); + } + return zDefault; + } + public MiniData getDataParam(String zParamName) throws CommandException { String hex = getParam(zParamName); return new MiniData(hex); } - - public JSONObject getJSONObjectParam(String zParamName) throws CommandException{ if(!existsParam(zParamName)) { throw new CommandException("param not specified : "+zParamName); @@ -199,6 +206,24 @@ public JSONArray getJSONArrayParam(String zParamName) throws CommandException { return (JSONArray) mParams.get(zParamName); } + public String getAddressParam(String zParamName) throws CommandException { + if(!existsParam(zParamName)) { + throw new CommandException("param not specified : "+zParamName); + } + + String address = getParam(zParamName); + if(address.toLowerCase().startsWith("mx")) { + //Convert back to normal hex.. + try { + address = Address.convertMinimaAddress(address).to0xString(); + }catch(IllegalArgumentException exc) { + throw new CommandException(exc.toString()); + } + } + + return address; + } + public boolean isParamJSONObject(String zParamName) { if(existsParam(zParamName)) { Object obj = mParams.get(zParamName); diff --git a/src/org/minima/system/commands/base/backup.java b/src/org/minima/system/commands/base/backup.java index 539491f0e..9640c87a1 100644 --- a/src/org/minima/system/commands/base/backup.java +++ b/src/org/minima/system/commands/base/backup.java @@ -6,18 +6,19 @@ import java.util.zip.GZIPOutputStream; import org.minima.database.MinimaDB; +import org.minima.objects.base.MiniByte; import org.minima.objects.base.MiniData; import org.minima.system.commands.Command; +import org.minima.system.commands.CommandException; import org.minima.system.params.GeneralParams; import org.minima.utils.MiniFile; import org.minima.utils.MiniFormat; -import org.minima.utils.MinimaLogger; import org.minima.utils.json.JSONObject; public class backup extends Command { public backup() { - super("backup","(file:) - Backup the entire system. Uses a timestamped name by default"); + super("backup","(file:) (complete:false|true) - Backup the system. Uses a timestamped name by default"); } @Override @@ -28,6 +29,8 @@ public JSONObject runCommand() throws Exception { if(file.equals("")) { file = "minima-backup-"+System.currentTimeMillis()+".bak.gz"; } + + boolean complete = getBooleanParam("complete", false); //Does it exist.. File backupfile = new File(file); @@ -35,6 +38,10 @@ public JSONObject runCommand() throws Exception { backupfile.delete(); } + ///Base folder + File backupfolder = new File(GeneralParams.DATA_FOLDER,"backup"); + backupfolder.mkdirs(); + //Lock the DB MinimaDB.getDB().readLock(true); @@ -43,23 +50,11 @@ public JSONObject runCommand() throws Exception { //Save the current state.. MinimaDB.getDB().saveState(); - ///Base folder - File backupfolder = new File(GeneralParams.DATA_FOLDER,"backup"); - backupfolder.mkdirs(); - //Write the SQL Dbs File walletfile = new File(backupfolder,"wallet.sql"); MinimaDB.getDB().getWallet().backupToFile(walletfile); MiniData walletata = new MiniData(MiniFile.readCompleteFile(walletfile)); - File txpowdb = new File(backupfolder,"txpowdb.sql"); - MinimaDB.getDB().getTxPoWDB().getSQLDB().backupToFile(txpowdb); - MiniData txpowdata = new MiniData(MiniFile.readCompleteFile(txpowdb)); - - File archivedb = new File(backupfolder,"archive.sql"); - MinimaDB.getDB().getArchive().backupToFile(archivedb); - MiniData archivedata = new MiniData(MiniFile.readCompleteFile(archivedb)); - File cascade = new File(backupfolder,"cascade.bak"); MinimaDB.getDB().getCascade().saveDB(cascade); MiniData cascadedata = new MiniData(MiniFile.readCompleteFile(cascade)); @@ -73,23 +68,44 @@ public JSONObject runCommand() throws Exception { MiniData userdata = new MiniData(MiniFile.readCompleteFile(userdb)); File p2pdb = new File(backupfolder,"p2p.bak"); - MinimaDB.getDB().getUserDB().saveDB(p2pdb); + MinimaDB.getDB().getP2PDB().saveDB(p2pdb); MiniData p2pdata = new MiniData(MiniFile.readCompleteFile(p2pdb)); - + + //For Complete.. + File txpowdb = new File(backupfolder,"txpowdb.sql"); + File archivedb = new File(backupfolder,"archive.sql"); + + MiniData txpowdata = null; + MiniData archivedata = null; + + if(complete) { + MinimaDB.getDB().getTxPoWDB().getSQLDB().backupToFile(txpowdb); + txpowdata = new MiniData(MiniFile.readCompleteFile(txpowdb)); + + MinimaDB.getDB().getArchive().backupToFile(archivedb); + archivedata = new MiniData(MiniFile.readCompleteFile(archivedb)); + } + //Now create the streams to save these FileOutputStream fos = new FileOutputStream(backupfile); GZIPOutputStream gzos = new GZIPOutputStream(fos); DataOutputStream dos = new DataOutputStream(gzos); - + + //Is it Complete + MiniByte.WriteToStream(dos, complete); + //And now put ALL of those files into a single file.. walletata.writeDataStream(dos); - txpowdata.writeDataStream(dos); - archivedata.writeDataStream(dos); cascadedata.writeDataStream(dos); chaindata.writeDataStream(dos); userdata.writeDataStream(dos); p2pdata.writeDataStream(dos); + if(complete) { + txpowdata.writeDataStream(dos); + archivedata.writeDataStream(dos); + } + //All done.. dos.close(); gzos.close(); @@ -97,18 +113,25 @@ public JSONObject runCommand() throws Exception { //The total uncompressed size.. long total = walletfile.length()+ - txpowdb.length()+ - archivedb.length()+ cascade.length()+ chain.length()+ userdb.length()+ p2pdb.length(); + if(complete) { + total += txpowdb.length()+ + cascade.length(); + } + //Get all the individual File sizes.. JSONObject files = new JSONObject(); files.put("wallet", MiniFormat.formatSize(walletfile.length())); - files.put("txpowdb", MiniFormat.formatSize(txpowdb.length())); - files.put("archive", MiniFormat.formatSize(archivedb.length())); + + if(complete) { + files.put("txpowdb", MiniFormat.formatSize(txpowdb.length())); + files.put("archive", MiniFormat.formatSize(archivedb.length())); + } + files.put("cascade", MiniFormat.formatSize(cascade.length())); files.put("chain", MiniFormat.formatSize(chain.length())); files.put("user", MiniFormat.formatSize(userdb.length())); @@ -127,12 +150,23 @@ public JSONObject runCommand() throws Exception { MiniFile.deleteFileOrFolder(GeneralParams.DATA_FOLDER, backupfolder); }catch(Exception exc) { - MinimaLogger.log(exc); + + //Unlock DB.. + MinimaDB.getDB().readLock(false); + + //Delete backup folder + MiniFile.deleteFileOrFolder(GeneralParams.DATA_FOLDER, backupfolder); + + //Throw an error to notify user.. + throw new CommandException(exc.toString()); } //Unlock.. MinimaDB.getDB().readLock(false); + //Delete backup folder + MiniFile.deleteFileOrFolder(GeneralParams.DATA_FOLDER, backupfolder); + return ret; } diff --git a/src/org/minima/system/commands/base/balance.java b/src/org/minima/system/commands/base/balance.java index 0021784d8..a224a7118 100644 --- a/src/org/minima/system/commands/base/balance.java +++ b/src/org/minima/system/commands/base/balance.java @@ -44,7 +44,7 @@ public JSONObject runCommand() throws Exception{ //Get the wallet.. to find the sendable coins.. Wallet wdb = MinimaDB.getDB().getWallet(); - ArrayList keys = wdb.getAllRelevant(); + ArrayList keys = wdb.getAllRelevant(false); ArrayList sendableaddresses = new ArrayList<>(); for(KeyRow key : keys) { if(!key.getPublicKey().equals("")) { diff --git a/src/org/minima/system/commands/base/burn.java b/src/org/minima/system/commands/base/burn.java new file mode 100644 index 000000000..869b52344 --- /dev/null +++ b/src/org/minima/system/commands/base/burn.java @@ -0,0 +1,209 @@ +package org.minima.system.commands.base; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; + +import org.minima.database.MinimaDB; +import org.minima.database.txpowdb.TxPoWDB; +import org.minima.database.txpowtree.TxPoWTreeNode; +import org.minima.objects.Transaction; +import org.minima.objects.TxPoW; +import org.minima.objects.base.MiniData; +import org.minima.objects.base.MiniNumber; +import org.minima.system.commands.Command; +import org.minima.utils.MinimaLogger; +import org.minima.utils.json.JSONObject; + +public class burn extends Command { + + MiniNumber mMinburn = MiniNumber.BILLION; + MiniNumber mMaxburn = MiniNumber.ZERO; + MiniNumber mBurnTot = MiniNumber.ZERO; + MiniNumber mBurnCount = MiniNumber.ZERO; + ArrayList mValues = new ArrayList<>(); + + MiniNumber mMinburn10 = MiniNumber.BILLION; + MiniNumber mMaxburn10 = MiniNumber.ZERO; + MiniNumber mBurnTot10 = MiniNumber.ZERO; + MiniNumber mBurnCount10 = MiniNumber.ZERO; + ArrayList mValues10 = new ArrayList<>(); + + MiniNumber mMinburn50 = MiniNumber.BILLION; + MiniNumber mMaxburn50 = MiniNumber.ZERO; + MiniNumber mBurnTot50 = MiniNumber.ZERO; + MiniNumber mBurnCount50 = MiniNumber.ZERO; + ArrayList mValues50 = new ArrayList<>(); + + public burn() { + super("burn","View Burn metrics"); + } + + @Override + public JSONObject runCommand() throws Exception { + JSONObject ret = getJSONReply(); + + String action = getParam("action", "list"); + + JSONObject response = new JSONObject(); + + TxPoWDB txpdb = MinimaDB.getDB().getTxPoWDB(); + + if(action.equals("list")) { + + //Get the tip.. + TxPoWTreeNode tip = MinimaDB.getDB().getTxPoWTree().getTip(); + + //Cycle back through.. + int counter=0; + while(tip != null && counter<50) { + + //Get the block + TxPoW tiptxpow = tip.getTxPoW(); + + //Adjust Burn values.. + checkBurn(tip.getTxPoW(), counter); + + //And now all the transaction in the block + ArrayList txns = tiptxpow.getBlockTransactions(); + for(MiniData txn : txns) { + + //Get the Transaction + TxPoW txp = txpdb.getTxPoW(txn.to0xString()); + + //Adjust Burn values.. + checkBurn(txp, counter); + } + + //And check the parent + tip = tip.getParent(); + counter++; + } + + if(mBurnCount.isEqual(MiniNumber.ZERO)) { + mMinburn = MiniNumber.ZERO; + } + if(mBurnCount10.isEqual(MiniNumber.ZERO)) { + mMinburn10 = MiniNumber.ZERO; + } + if(mBurnCount50.isEqual(MiniNumber.ZERO)) { + mMinburn50 = MiniNumber.ZERO; + } + + JSONObject block1 = new JSONObject(); + block1.put("txns", mBurnCount); + block1.put("max", mMaxburn); + block1.put("med", getMediaValue(mValues)); + if(mBurnCount.isMore(MiniNumber.ZERO)) { + block1.put("avg", mBurnTot.div(mBurnCount)); + }else { + block1.put("avg", MiniNumber.ZERO); + } + block1.put("min", mMinburn); + + JSONObject block10 = new JSONObject(); + block10.put("txns", mBurnCount10); + block10.put("max", mMaxburn10); + block10.put("med", getMediaValue(mValues10)); + if(mBurnCount10.isMore(MiniNumber.ZERO)) { + block10.put("avg", mBurnTot10.div(mBurnCount10)); + }else { + block10.put("avg", MiniNumber.ZERO); + } + block10.put("min", mMinburn10); + + JSONObject block50 = new JSONObject(); + block50.put("txns", mBurnCount50); + block50.put("max", mMaxburn50); + block50.put("med", getMediaValue(mValues50)); + if(mBurnCount50.isMore(MiniNumber.ZERO)) { + block50.put("avg", mBurnTot50.div(mBurnCount50)); + }else { + block50.put("avg", MiniNumber.ZERO); + } + block50.put("min", mMinburn50); + + response.put("1block",block1); + response.put("10block",block10); + response.put("50block",block50); + } + + ret.put("response", response); + + return ret; + } + + public void checkBurn(TxPoW zTxPoW, int counter) { + if(!zTxPoW.isTransaction()) { + return; + } + + MiniNumber burn = zTxPoW.getBurn(); + + if(counter == 0) { + if(burn.isMore(mMaxburn)) { + mMaxburn = burn; + } + if(burn.isLess(mMinburn)) { + mMinburn = burn; + } + + mBurnCount = mBurnCount.increment(); + mBurnTot = mBurnTot.add(burn); + mValues.add(burn); + } + + //And the Total for the last 10 + if(counter < 10) { + if(burn.isMore(mMaxburn10)) { + mMaxburn10 = burn; + } + if(burn.isLess(mMinburn10)) { + mMinburn10 = burn; + } + + mBurnCount10 = mBurnCount10.increment(); + mBurnTot10 = mBurnTot10.add(burn); + mValues10.add(burn); + } + + //And the last 50 + if(burn.isMore(mMaxburn50)) { + mMaxburn50 = burn; + } + if(burn.isLess(mMinburn50)) { + mMinburn50 = burn; + } + + mBurnCount50 = mBurnCount50.increment(); + mBurnTot50 = mBurnTot50.add(burn); + mValues50.add(burn); + } + + public MiniNumber getMediaValue(ArrayList zValues) { + + if(zValues.size()==0) { + return MiniNumber.ZERO; + } + + //Sort them + Collections.sort(zValues, new Comparator() { + @Override + public int compare(MiniNumber o1, MiniNumber o2) { + // TODO Auto-generated method stub + return o2.compareTo(o1); + } + }); + + //Get the median.. + int size = zValues.size(); + + return zValues.get(size/2); + } + + @Override + public Command getFunction() { + return new burn(); + } + +} diff --git a/src/org/minima/system/commands/base/coinimport.java b/src/org/minima/system/commands/base/coinimport.java index 1864a74bb..e088c4cbd 100644 --- a/src/org/minima/system/commands/base/coinimport.java +++ b/src/org/minima/system/commands/base/coinimport.java @@ -16,7 +16,7 @@ public class coinimport extends Command { public coinimport() { - super("coinimport","[data:] - Import a coin, and keep tracking it"); + super("coinimport","[data:] (track:false) - Import a coin, and keep tracking it"); } @Override @@ -95,9 +95,11 @@ public JSONObject runCommand() throws Exception { //Add to the total List of coins fro this block treenode.getAllCoins().add(newcoin); - //And set to relevant.. - treenode.getRelevantCoins().add(newcoin); - treenode.getRelevantCoinsEntries().add(newcoin.getMMREntryNumber()); + //And set to relevant.. track it.. + if(getBooleanParam("track", true)) { + treenode.getRelevantCoins().add(newcoin); + treenode.getRelevantCoinsEntries().add(newcoin.getMMREntryNumber()); + } //New root.. MMRData newroot = treenode.getMMR().getRoot(); diff --git a/src/org/minima/system/commands/base/file.java b/src/org/minima/system/commands/base/file.java new file mode 100644 index 000000000..f9c72c624 --- /dev/null +++ b/src/org/minima/system/commands/base/file.java @@ -0,0 +1,120 @@ +package org.minima.system.commands.base; + +import java.io.File; + +import org.minima.objects.base.MiniData; +import org.minima.system.commands.Command; +import org.minima.system.commands.CommandException; +import org.minima.system.params.GeneralParams; +import org.minima.utils.MiniFile; +import org.minima.utils.json.JSONArray; +import org.minima.utils.json.JSONObject; + +public class file extends Command { + + public file() { + super("file","[action:list|load|save|delete] [file:] (data:)- Load, save, delete or list files. / is root. Data is in 0xHEX format"); + } + + @Override + public JSONObject runCommand() throws Exception { + JSONObject ret = getJSONReply(); + + String action = getParam("action"); + String thefile = getParam("file"); + + //Check no back.. + if(thefile.indexOf("..")!=-1) { + throw new CommandException("No .. parent allowed in filename"); + } + + //The Root.. + File root = new File(GeneralParams.DATA_FOLDER,"minifiles"); + if(!root.exists()) { + root.mkdir(); + } + + //Which file are we listing.. + File touchfile = new File(root,thefile); + + if(action.equals("list")) { + + //Get the list of files.. + JSONArray allfiles = new JSONArray(); + File[] files = touchfile.listFiles(); + if(files!=null) { + + for(File ff : files) { + + JSONObject fjson = new JSONObject(); + fjson.put("name", ff.getName()); + fjson.put("directory", ff.isDirectory()); + fjson.put("size", ff.length()); + + allfiles.add(fjson); + } + } + + //Now get the details.. + JSONObject resp = new JSONObject(); + resp.put("found", allfiles.size()); + resp.put("files", allfiles); + ret.put("response", resp); + + }else if(action.equals("save")) { + + //Make sure the parent exists.. + File parent = touchfile.getParentFile(); + parent.mkdirs(); + + //Get the adta param.. + MiniData data = getDataParam("data"); + + //Save this data.. + MiniFile.writeDataToFile(touchfile, data.getBytes()); + + //Now get the details.. + JSONObject resp = new JSONObject(); + resp.put("name", thefile); + resp.put("size", touchfile.length()); + ret.put("response", resp); + + }else if(action.equals("load")) { + + //Load the file.. + byte[] datafile = MiniFile.readCompleteFile(touchfile); + + //The MinData object + MiniData filedata = new MiniData(datafile); + + //Now get the details.. + JSONObject resp = new JSONObject(); + resp.put("name", thefile); + resp.put("size", touchfile.length()); + resp.put("data", filedata.to0xString()); + ret.put("response", resp); + + }else if(action.equals("delete")) { + + //Delete the file.. + MiniFile.deleteFileOrFolder(root.getAbsolutePath(), touchfile); + + //Now get the details.. + JSONObject resp = new JSONObject(); + resp.put("name", thefile); + resp.put("status", "deleted"); + ret.put("response", resp); + + }else { + throw new CommandException("Invalid file function : "+action); + } + + return ret; + } + + @Override + public Command getFunction() { + return new file(); + } + +} diff --git a/src/org/minima/system/commands/base/getaddress.java b/src/org/minima/system/commands/base/getaddress.java index 4d1b9d5ff..74415c215 100644 --- a/src/org/minima/system/commands/base/getaddress.java +++ b/src/org/minima/system/commands/base/getaddress.java @@ -1,18 +1,15 @@ package org.minima.system.commands.base; -import java.util.ArrayList; - import org.minima.database.MinimaDB; import org.minima.database.wallet.KeyRow; import org.minima.database.wallet.Wallet; import org.minima.system.commands.Command; -import org.minima.utils.json.JSONArray; import org.minima.utils.json.JSONObject; public class getaddress extends Command { public getaddress() { - super("getaddress","(type:single|list) - Get a Minima address to receive funds"); + super("getaddress","Get one of your default Minima addresses / keys"); } @Override @@ -24,27 +21,11 @@ public JSONObject runCommand() throws Exception{ String type = getParam("type","single"); - if(type.equals("single")) { - //Get an existing address - KeyRow krow = wallet.getKey(); - - //Put the details in the response.. - ret.put("response", krow.toJSON()); - - }else { - - //Get all the keys - ArrayList allkeys = wallet.getAllRelevant(); - - //Add them to an array - JSONArray keyarray =new JSONArray(); - for(KeyRow key : allkeys) { - keyarray.add(key.toJSON()); - } + //Get an existing address + KeyRow krow = wallet.getDefaultKeyAddress(); - //Put the details in the response.. - ret.put("response", keyarray); - } + //Put the details in the response.. + ret.put("response", krow.toJSON()); return ret; } diff --git a/src/org/minima/system/commands/base/newaddress.java b/src/org/minima/system/commands/base/newaddress.java index 36787120f..27dac33d0 100644 --- a/src/org/minima/system/commands/base/newaddress.java +++ b/src/org/minima/system/commands/base/newaddress.java @@ -9,7 +9,7 @@ public class newaddress extends Command { public newaddress() { - super("newaddress","Create a new address or public key"); + super("newaddress","Create a new address or public key that will not be not used for anything else (change address)"); } @Override @@ -20,7 +20,7 @@ public JSONObject runCommand() throws Exception{ Wallet wallet = MinimaDB.getDB().getWallet(); //Create a new address - KeyRow krow = wallet.createNewKey(); + KeyRow krow = wallet.createNewKey(true); //Put the details in the response.. ret.put("response", krow.toJSON()); diff --git a/src/org/minima/system/commands/base/restore.java b/src/org/minima/system/commands/base/restore.java index 5c3fbaf5c..d2abe0773 100644 --- a/src/org/minima/system/commands/base/restore.java +++ b/src/org/minima/system/commands/base/restore.java @@ -7,6 +7,7 @@ import java.util.zip.GZIPInputStream; import org.minima.database.MinimaDB; +import org.minima.objects.base.MiniByte; import org.minima.objects.base.MiniData; import org.minima.system.Main; import org.minima.system.commands.Command; @@ -50,13 +51,14 @@ public JSONObject runCommand() throws Exception { GZIPInputStream gzin = new GZIPInputStream(bais); DataInputStream dis = new DataInputStream(gzin); + //Is this a complete backup.. + boolean complete = MiniByte.ReadFromStream(dis).isTrue(); + //The total size of files.. - long total = 0; + long total = 1; //Read in each section.. total += readNextBackup(new File(restorefolder,"wallet.sql"), dis); - total += readNextBackup(new File(restorefolder,"txpowdb.sql"), dis); - total += readNextBackup(new File(restorefolder,"archive.sql"), dis); //The rest write directly File basedb = MinimaDB.getDB().getBaseDBFolder(); @@ -67,8 +69,23 @@ public JSONObject runCommand() throws Exception { //Now load the sql MinimaDB.getDB().getWallet().restoreFromFile(new File(restorefolder,"wallet.sql")); - MinimaDB.getDB().getTxPoWDB().getSQLDB().restoreFromFile(new File(restorefolder,"txpowdb.sql")); - MinimaDB.getDB().getArchive().restoreFromFile(new File(restorefolder,"archive.sql")); + + //Complete + if(complete) { + total += readNextBackup(new File(restorefolder,"txpowdb.sql"), dis); + total += readNextBackup(new File(restorefolder,"archive.sql"), dis); + + MinimaDB.getDB().getTxPoWDB().getSQLDB().restoreFromFile(new File(restorefolder,"txpowdb.sql")); + MinimaDB.getDB().getArchive().restoreFromFile(new File(restorefolder,"archive.sql")); + }else { + + //Close and Wipe those.. + MinimaDB.getDB().getTxPoWDB().getSQLDB().saveDB(); + MinimaDB.getDB().getTxPoWDB().getSQLDB().getSQLFile().delete(); + + MinimaDB.getDB().getArchive().saveDB(); + MinimaDB.getDB().getArchive().getSQLFile().delete(); + } //Close up shop.. dis.close(); @@ -85,7 +102,14 @@ public JSONObject runCommand() throws Exception { ret.put("message", "Restart Minima for restore to take effect!"); //Now save the Databases.. - MinimaDB.getDB().saveSQL(); + if(complete) { + MinimaDB.getDB().saveSQL(); + }else { + MinimaDB.getDB().saveWalletSQL(); + } + + //Don't do the usual shutdown hook + Main.getInstance().setHasShutDown(); //And NOW shut down.. Main.getInstance().stopMessageProcessor(); diff --git a/src/org/minima/system/commands/base/runscript.java b/src/org/minima/system/commands/base/runscript.java index 4f13a3bcf..9ba274ff0 100644 --- a/src/org/minima/system/commands/base/runscript.java +++ b/src/org/minima/system/commands/base/runscript.java @@ -142,12 +142,12 @@ public JSONObject runCommand() throws Exception{ //Set trhe Script.. contract.setGlobalVariable("@SCRIPT", new StringValue(script)); - //Is there enough for @BLOCKDIFF - if(globals.containsKey("@BLOCK") && globals.containsKey("@INBLOCK") && !globals.containsKey("@BLOCKDIFF")) { + //Is there enough for @COINAGE + if(globals.containsKey("@BLOCK") && globals.containsKey("@CREATED") && !globals.containsKey("@COINAGE")) { MiniNumber block = new MiniNumber((String)globals.get("@BLOCK")); - MiniNumber inblock = new MiniNumber((String)globals.get("@INBLOCK")); + MiniNumber inblock = new MiniNumber((String)globals.get("@CREATED")); MiniNumber blockdiff = block.sub(inblock); - globals.put("@BLOCKDIFF", blockdiff.toString()); + globals.put("@COINAGE", blockdiff.toString()); } //Set the Globals.. diff --git a/src/org/minima/system/commands/base/send.java b/src/org/minima/system/commands/base/send.java index ad06ef2f5..b5ae60876 100644 --- a/src/org/minima/system/commands/base/send.java +++ b/src/org/minima/system/commands/base/send.java @@ -6,6 +6,7 @@ import org.minima.database.mmr.MMRProof; import org.minima.database.txpowdb.TxPoWDB; import org.minima.database.txpowtree.TxPoWTreeNode; +import org.minima.database.userprefs.txndb.TxnRow; import org.minima.database.wallet.KeyRow; import org.minima.database.wallet.Wallet; import org.minima.objects.Coin; @@ -25,32 +26,40 @@ import org.minima.system.brains.TxPoWSearcher; import org.minima.system.commands.Command; import org.minima.system.commands.CommandException; +import org.minima.system.commands.txn.txnutils; import org.minima.system.params.GlobalParams; -import org.minima.utils.Crypto; import org.minima.utils.json.JSONObject; public class send extends Command { public send() { - super("send","[address:] [amount:] (floating:) (tokenid:) (state:{}) - Send Minima or Tokens to an address"); + super("send","[address:Mx..|0x..] [amount:] (tokenid:) (state:{}) (burn:) (split:) - Send Minima or Tokens to an address"); } @Override public JSONObject runCommand() throws Exception { JSONObject ret = getJSONReply(); - //Get the details - String address = (String)getParams().get("address"); - String amount = (String)getParams().get("amount"); + //Get the address + String address = getAddressParam("address"); + + //How much to send + String amount = getParam("amount"); - if(address==null || amount==null) { - throw new CommandException("MUST specify adress and amount"); + //Is there a burn.. + MiniNumber burn = getNumberParam("burn",MiniNumber.ZERO); + if(burn.isLess(MiniNumber.ZERO)) { + throw new CommandException("Cannot have negative burn "+burn.toString()); } - //Is the coin floating - boolean floating = getBooleanParam("floating",false); + //Are we splitting the outputs + MiniNumber split = getNumberParam("split", MiniNumber.ONE); + if(split.isLess(MiniNumber.ONE) || split.isMore(MiniNumber.TWENTY)) { + throw new CommandException("Split outputs from 1 to 20"); + } + //Get the State JSONObject state = new JSONObject(); if(existsParam("state")) { state = getJSONObjectParam("state"); @@ -182,9 +191,15 @@ public JSONObject runCommand() throws Exception { //Create a list of the required signatures ArrayList reqsigs = new ArrayList<>(); + //Which Coins are added + ArrayList addedcoinid = new ArrayList<>(); + //Add the MMR proofs for the coins.. for(Coin input : currentcoins) { + //May need it for BURN + addedcoinid.add(input.getCoinID().to0xString()); + //Get the proof.. MMRProof proof = mmrnode.getMMR().getProofToPeak(input.getMMREntryNumber()); @@ -231,22 +246,27 @@ public JSONObject runCommand() throws Exception { } } - //Create the output - Coin recipient = new Coin(Coin.COINID_OUTPUT, sendaddress, sendamount, Token.TOKENID_MINIMA, floating, true); - - //Do we need to add the Token.. - if(!tokenid.equals("0x00")) { - recipient.resetTokenID(new MiniData(tokenid)); - recipient.setToken(token); + //Are we splitting the outputs + int isplit = split.getAsInt(); + MiniNumber splitamount = sendamount.div(split); + for(int i=0;i reqsigs = new ArrayList<>(); + //Which Coins are added + ArrayList addedcoinid = new ArrayList<>(); + //Add the MMR proofs for the coins.. for(Coin input : currentcoins) { + //Keep for burn calc + addedcoinid.add(input.getCoinID().to0xString()); + //Get the proof.. MMRProof proof = mmrnode.getMMR().getProofToPeak(input.getMMREntryNumber()); @@ -260,7 +270,7 @@ public JSONObject runCommand() throws Exception { } //Now add the output.. - Coin recipient = new Coin(sendaddress, sendamount, Token.TOKENID_CREATE); + Coin recipient = new Coin(Coin.COINID_OUTPUT, sendaddress, sendamount, Token.TOKENID_CREATE, true); //Set the Create Token Details.. recipient.setToken(createtoken); @@ -271,7 +281,7 @@ public JSONObject runCommand() throws Exception { //Do we need to send change.. if(change.isMore(MiniNumber.ZERO)) { //Create a new address - KeyRow newwalletaddress = MinimaDB.getDB().getWallet().createNewKey(); + KeyRow newwalletaddress = MinimaDB.getDB().getWallet().getDefaultKeyAddress(); MiniData chgaddress = new MiniData(newwalletaddress.getAddress()); Coin changecoin = new Coin(Coin.COINID_OUTPUT, chgaddress, change, Token.TOKENID_MINIMA); @@ -297,21 +307,38 @@ public JSONObject runCommand() throws Exception { transaction.addStateVariable(sv); } + //Compute the correct CoinID + TxPoWGenerator.precomputeTransactionCoinID(transaction); + //Calculate the TransactionID.. - MiniData transid = Crypto.getInstance().hashObject(transaction); + transaction.calculateTransactionID(); //Now that we have constructed the transaction - lets sign it.. for(String priv : reqsigs) { //Use the wallet.. - Signature signature = walletdb.sign(priv, transid); + Signature signature = walletdb.sign(priv, transaction.getTransactionID()); //Add it.. witness.addSignature(signature); } - //Now create a complete TxPOW - TxPoW txpow = TxPoWGenerator.generateTxPoW(transaction, witness); + //The final TxPoW + TxPoW txpow = null; + + //Is there a BURN.. + if(burn.isMore(MiniNumber.ZERO)) { + + //Create a Burn Transaction + TxnRow burntxn = txnutils.createBurnTransaction(addedcoinid,transaction.getTransactionID(),burn); + + //Now create a complete TxPOW + txpow = TxPoWGenerator.generateTxPoW(transaction, witness, burntxn.getTransaction(), burntxn.getWitness()); + + }else { + //Now create a complete TxPOW + txpow = TxPoWGenerator.generateTxPoW(transaction, witness); + } //Calculate the size.. txpow.calculateTXPOWID(); diff --git a/src/org/minima/system/commands/base/tutorial.java b/src/org/minima/system/commands/base/tutorial.java index 70c790c17..f06462d38 100644 --- a/src/org/minima/system/commands/base/tutorial.java +++ b/src/org/minima/system/commands/base/tutorial.java @@ -46,7 +46,7 @@ public JSONObject runCommand() throws Exception{ + "BOOLEAN ::= TRUE | FALSE\n" + "FALSE ::= 0\n" + "TRUE ::= NOT FALSE\n" - + "GLOBAL ::= @BLOCK | @INBLOCK | @BLOCKDIFF | @INPUT |\n" + + "GLOBAL ::= @BLOCK | @BLOCKTIME | @CREATED | @COINAGE | @INPUT |\n" + " @AMOUNT | @ADDRESS | @TOKENID | @COINID |\n" + " @SCRIPT | @TOTIN | @TOTOUT\n" + "FUNCTION ::= FUNC ( EXPRESSION_1 EXPRESSION_2 .. EXPRESSION_n )\n" @@ -56,6 +56,7 @@ public JSONObject runCommand() throws Exception{ + " ABS | CEIL | FLOOR | MIN | MAX | INC | DEC | SIGDIG | POW |\n" + " BITSET | BITGET | BITCOUNT | PROOF | KECCAK | SHA2 | SHA3 |\n" + " SIGNEDBY | MULTISIG | CHECKSIG |\n" + + " FUNCTION | SUMINPUT | SUMOUTPUT |\n" + " GETOUTADDR | GETOUTAMT | GETOUTTOK | VERIFYOUT |\n" + " GETINADDR | GETINAMT | GETINTOK | GETINID | VERIFYIN |\n" + " STATE | PREVSTATE | SAMESTATE\n" @@ -63,8 +64,8 @@ public JSONObject runCommand() throws Exception{ + "Globals\n" + "\n" + "@BLOCK : Block number this transaction is in\n" - + "@INBLOCK : Block number when this output was created\n" - + "@BLOCKDIFF : Difference between @BLOCK and INBLOCK\n" + + "@CREATED : Block number when this coin was created\n" + + "@COINAGE : Difference between @BLOCK and @CREATED\n" + "@INPUT : Input number in the transaction\n" + "@COINID : CoinID of this input\n" + "@AMOUNT : Amount of this input\n" @@ -91,7 +92,7 @@ public JSONObject runCommand() throws Exception{ + "OVERWRITE ( HEX NUMBER HEX NUMBER NUMBER)\n" + "Copy bytes from the first HEX and pos to the second HEX and pos, length the last NUMBER\n" + "\n" - + "GET ( NUMBER NUMBER .. NUMBER )\n" + + "GET ( VALUE1 VALUE2 .. VALUEn )\n" + "Return the array value set with LET ( EXPRESSION EXPRESSION .. EXPRESSION )\n" + "\n" + "ADDRESS ( STRING )\n" @@ -201,6 +202,12 @@ public JSONObject runCommand() throws Exception{ + "VERIFYIN ( NUMBER HEX NUMBER HEX )\n" + "Verify the input has the specified address, amount and tokenid\n" + "\n" + + "SUMINPUTS ( HEX )\n" + + "Sum the input values of this token type\n" + + "\n" + + "SUMOUTPUTS ( HEX )\n" + + "Sum the output values of this token type\n" + + "\n" + "STATE ( NUMBER )\n" + "Return the state value for the given number\n" + "\n" @@ -209,6 +216,9 @@ public JSONObject runCommand() throws Exception{ + "\n" + "SAMESTATE ( NUMBER NUMBER )\n" + "Return TRUE if the previous state and current state are the same for the start and end positions\n" + + "\n" + + "FUNCTION ( STRING VALUE1 VALUE2.. VALUEn )\n" + + "Generic Function. Run the script after replacing $1, $2.. $n with the provided parameters and use returnvalue as the returned result\n" + ""; //Add balance.. diff --git a/src/org/minima/system/commands/base/tutorial.txt b/src/org/minima/system/commands/base/tutorial.txt index d410bc464..e6666e05b 100644 --- a/src/org/minima/system/commands/base/tutorial.txt +++ b/src/org/minima/system/commands/base/tutorial.txt @@ -30,7 +30,7 @@ STRING ::= [UTF8_String] BOOLEAN ::= TRUE | FALSE FALSE ::= 0 TRUE ::= NOT FALSE -GLOBAL ::= @BLOCK | @INBLOCK | @BLOCKDIFF | @INPUT | +GLOBAL ::= @BLOCK | @BLOCKTIME | @CREATED | @COINAGE | @INPUT | @AMOUNT | @ADDRESS | @TOKENID | @COINID | @SCRIPT | @TOTIN | @TOTOUT FUNCTION ::= FUNC ( EXPRESSION_1 EXPRESSION_2 .. EXPRESSION_n ) @@ -40,6 +40,7 @@ FUNC ::= CONCAT | LEN | REV | SUBSET | GET | OVERWRITE | ABS | CEIL | FLOOR | MIN | MAX | INC | DEC | SIGDIG | POW | BITSET | BITGET | BITCOUNT | PROOF | KECCAK | SHA2 | SHA3 | SIGNEDBY | MULTISIG | CHECKSIG | + FUNCTION | SUMINPUT | SUMOUTPUT | GETOUTADDR | GETOUTAMT | GETOUTTOK | VERIFYOUT | GETINADDR | GETINAMT | GETINTOK | GETINID | VERIFYIN | STATE | PREVSTATE | SAMESTATE @@ -47,8 +48,8 @@ FUNC ::= CONCAT | LEN | REV | SUBSET | GET | OVERWRITE | Globals @BLOCK : Block number this transaction is in -@INBLOCK : Block number when this output was created -@BLOCKDIFF : Difference between @BLOCK and INBLOCK +@CREATED : Block number when this coin was created +@COINAGE : Difference between @BLOCK and @CREATED @INPUT : Input number in the transaction @COINID : CoinID of this input @AMOUNT : Amount of this input @@ -75,14 +76,14 @@ Return the HEX subset of the data - start - length OVERWRITE ( HEX NUMBER HEX NUMBER NUMBER) Copy bytes from the first HEX and pos to the second HEX and pos, length the last NUMBER -GET ( NUMBER NUMBER .. NUMBER ) +GET ( VALUE1 VALUE2 .. VALUEn ) Return the array value set with LET ( EXPRESSION EXPRESSION .. EXPRESSION ) ADDRESS ( STRING ) Return the address of the script REPLACE ( STRING STRING STRING ) -Replace in 1st string all occurrence of 2nd string with 3rd +Replace in the 1st string all occurrence of 2nd string with 3rd SUBSTR ( STRING NUMBER NUMBER ) Get the substring @@ -185,6 +186,12 @@ Return the token id of the specified input VERIFYIN ( NUMBER HEX NUMBER HEX ) Verify the input has the specified address, amount and tokenid +SUMINPUTS ( HEX ) +Sum the input values of this token type + +SUMOUTPUTS ( HEX ) +Sum the output values of this token type + STATE ( NUMBER ) Return the state value for the given number @@ -194,3 +201,5 @@ Return the state value stored in the coin MMR data - when the coin was created SAMESTATE ( NUMBER NUMBER ) Return TRUE if the previous state and current state are the same for the start and end positions +FUNCTION ( STRING VALUE1 VALUE2.. VALUEn ) +Generic Function. Run the script after replacing $1, $2.. $n with the provided parameters and use returnvalue as the returned result diff --git a/src/org/minima/system/commands/help.java b/src/org/minima/system/commands/help.java index 7f3211e98..715b898ec 100644 --- a/src/org/minima/system/commands/help.java +++ b/src/org/minima/system/commands/help.java @@ -3,9 +3,11 @@ import org.minima.system.commands.base.automine; import org.minima.system.commands.base.backup; import org.minima.system.commands.base.balance; +import org.minima.system.commands.base.burn; import org.minima.system.commands.base.coinexport; import org.minima.system.commands.base.coinimport; import org.minima.system.commands.base.cointrack; +import org.minima.system.commands.base.getaddress; import org.minima.system.commands.base.hash; import org.minima.system.commands.base.hashtest; import org.minima.system.commands.base.incentivecash; @@ -31,13 +33,12 @@ import org.minima.system.commands.network.rpc; import org.minima.system.commands.network.sshtunnel; import org.minima.system.commands.network.webhooks; -import org.minima.system.commands.persistent.file; -import org.minima.system.commands.persistent.sql; import org.minima.system.commands.search.coins; import org.minima.system.commands.search.keys; import org.minima.system.commands.search.txpow; import org.minima.system.commands.signatures.sign; import org.minima.system.commands.signatures.verify; +import org.minima.system.commands.txn.txnbasics; import org.minima.system.commands.txn.txncheck; import org.minima.system.commands.txn.txnclear; import org.minima.system.commands.txn.txncreate; @@ -69,6 +70,7 @@ public JSONObject runCommand() throws Exception{ addCommand(details, new status()); addCommand(details, new printtree()); + addCommand(details, new burn()); addCommand(details, new trace()); addCommand(details, new automine()); addCommand(details, new hashtest()); @@ -79,14 +81,15 @@ public JSONObject runCommand() throws Exception{ addCommand(details, new tokens()); addCommand(details, new keys()); + addCommand(details, new getaddress()); addCommand(details, new newaddress()); addCommand(details, new send()); addCommand(details, new balance()); addCommand(details, new tokencreate()); addCommand(details, new hash()); - addCommand(details, new file()); - addCommand(details, new sql()); +// addCommand(details, new file()); +// addCommand(details, new sql()); addCommand(details, new scripts()); addCommand(details, new runscript()); @@ -104,6 +107,7 @@ public JSONObject runCommand() throws Exception{ addCommand(details, new txnlist()); addCommand(details, new txncreate()); + addCommand(details, new txnbasics()); addCommand(details, new txndelete()); addCommand(details, new txncheck()); addCommand(details, new txninput()); diff --git a/src/org/minima/system/commands/network/network.java b/src/org/minima/system/commands/network/network.java index 807293a36..4d6eafc8c 100644 --- a/src/org/minima/system/commands/network/network.java +++ b/src/org/minima/system/commands/network/network.java @@ -4,6 +4,7 @@ import org.minima.system.Main; import org.minima.system.commands.Command; +import org.minima.system.commands.CommandException; import org.minima.system.network.minima.NIOClientInfo; import org.minima.utils.json.JSONArray; import org.minima.utils.json.JSONObject; @@ -11,24 +12,39 @@ public class network extends Command { public network() { - super("network","Show network status"); + super("network","(action:list|restart) - Show network status or restart"); } @Override - public JSONObject runCommand() { + public JSONObject runCommand() throws Exception { JSONObject ret = getJSONReply(); - //Get the NIO Details - ArrayList clients = Main.getInstance().getNetworkManager().getNIOManager().getAllConnectionInfo(); + String action = getParam("action", "list"); - //Create a JSONArray - JSONArray clarr = new JSONArray(); - for(NIOClientInfo info : clients) { - clarr.add(info.toJSON()); - } + if(action.equals("list")) { + //Get the NIO Details + ArrayList clients = Main.getInstance().getNetworkManager().getNIOManager().getAllConnectionInfo(); + + //Create a JSONArray + JSONArray clarr = new JSONArray(); + for(NIOClientInfo info : clients) { + clarr.add(info.toJSON()); + } + + //Add to the response + ret.put("response", clarr); + + }else if(action.equals("restart")) { + + //Send tyhe message to restart the network + Main.getInstance().PostMessage(Main.MAIN_NETRESTART); + + //Add to the response + ret.put("response", "Restarting.."); - //Add to the response - ret.put("response", clarr); + }else { + throw new CommandException("Invalid action"); + } return ret; } diff --git a/src/org/minima/system/commands/search/coins.java b/src/org/minima/system/commands/search/coins.java index f6a91cc72..512c9f34f 100644 --- a/src/org/minima/system/commands/search/coins.java +++ b/src/org/minima/system/commands/search/coins.java @@ -23,12 +23,17 @@ public JSONObject runCommand() throws Exception{ JSONObject ret = getJSONReply(); //Check a parameter specified + boolean hardsetrel = false; if(!existsParam("relevant") && !existsParam("coinid") && !existsParam("address") && !existsParam("tokenid")) { - throw new Exception("No parameters specified"); + hardsetrel = true; } //Get the txpowid boolean relevant = existsParam("relevant"); + if(hardsetrel) { + relevant = true; + } + boolean simple = getBooleanParam("sendable",false); boolean scoinid = existsParam("coinid"); @@ -46,7 +51,7 @@ public JSONObject runCommand() throws Exception{ boolean saddress = existsParam("address"); MiniData address = MiniData.ZERO_TXPOWID; if(saddress) { - address = new MiniData(getParam("address", "0x01")); + address = new MiniData(getAddressParam("address")); } boolean stokenid = existsParam("tokenid"); diff --git a/src/org/minima/system/commands/search/keys.java b/src/org/minima/system/commands/search/keys.java index 4197e2a93..3425b6f34 100644 --- a/src/org/minima/system/commands/search/keys.java +++ b/src/org/minima/system/commands/search/keys.java @@ -31,11 +31,11 @@ public JSONObject runCommand() throws Exception{ MiniData privdata = getDataParam("import"); //Create a new Key - wallet.createNewKey(privdata); + wallet.createNewKey(privdata, true); } //Get all the keys - ArrayList keys = wallet.getAllRelevant(); + ArrayList keys = wallet.getAllRelevant(false); JSONArray arr = new JSONArray(); for(KeyRow kr : keys) { diff --git a/src/org/minima/system/commands/search/txpow.java b/src/org/minima/system/commands/search/txpow.java index 8d25c60fc..9f30a5092 100644 --- a/src/org/minima/system/commands/search/txpow.java +++ b/src/org/minima/system/commands/search/txpow.java @@ -47,7 +47,7 @@ public JSONObject runCommand() throws Exception{ }else if(existsParam("address")) { - String address = getParam("address"); + String address = getAddressParam("address"); ArrayList txps = TxPoWSearcher.searchTxPoWviaAddress(new MiniData(address)); diff --git a/src/org/minima/system/commands/txn/txnbasics.java b/src/org/minima/system/commands/txn/txnbasics.java new file mode 100644 index 000000000..3772edf78 --- /dev/null +++ b/src/org/minima/system/commands/txn/txnbasics.java @@ -0,0 +1,56 @@ +package org.minima.system.commands.txn; + +import org.minima.database.MinimaDB; +import org.minima.database.userprefs.txndb.TxnDB; +import org.minima.database.userprefs.txndb.TxnRow; +import org.minima.objects.Transaction; +import org.minima.objects.Witness; +import org.minima.system.brains.TxPoWGenerator; +import org.minima.system.commands.Command; +import org.minima.system.commands.CommandException; +import org.minima.utils.json.JSONObject; + +public class txnbasics extends Command { + + public txnbasics() { + super("txnbasics","[id:] - Automatically set the MMR proofs and scripts for a txn"); + } + + @Override + public JSONObject runCommand() throws Exception { + JSONObject ret = getJSONReply(); + + TxnDB db = MinimaDB.getDB().getCustomTxnDB(); + + //The transaction + String id = getParam("id"); + TxnRow txnrow = db.getTransactionRow(id); + if(txnrow == null) { + throw new CommandException("Transaction not found : "+id); + } + + //Get the Transaction + Transaction trans = txnrow.getTransaction(); + Witness wit = txnrow.getWitness(); + + //Set the MMR data and Scripts - for coins you have have + txnutils.setMMRandScripts(trans, wit, false); + + //Compute the correct CoinID + TxPoWGenerator.precomputeTransactionCoinID(trans); + + //Calculate the TransactionID.. + trans.calculateTransactionID(); + + //All good.. + ret.put("response", txnrow.toJSON()); + + return ret; + } + + @Override + public Command getFunction() { + return new txnbasics(); + } + +} diff --git a/src/org/minima/system/commands/txn/txncheck.java b/src/org/minima/system/commands/txn/txncheck.java index b3d134d02..4d3c15cf4 100644 --- a/src/org/minima/system/commands/txn/txncheck.java +++ b/src/org/minima/system/commands/txn/txncheck.java @@ -3,14 +3,18 @@ import java.util.ArrayList; import org.minima.database.MinimaDB; +import org.minima.database.txpowtree.TxPoWTreeNode; import org.minima.database.userprefs.txndb.TxnDB; import org.minima.database.userprefs.txndb.TxnRow; import org.minima.objects.Coin; import org.minima.objects.Token; import org.minima.objects.Transaction; +import org.minima.objects.TxPoW; import org.minima.objects.Witness; import org.minima.objects.base.MiniData; import org.minima.objects.base.MiniNumber; +import org.minima.system.brains.TxPoWChecker; +import org.minima.system.brains.TxPoWGenerator; import org.minima.system.commands.Command; import org.minima.system.commands.CommandException; import org.minima.utils.json.JSONArray; @@ -115,6 +119,29 @@ public JSONObject runCommand() throws Exception { int sigs = txnrow.getWitness().getAllSignatures().size(); details.put("signatures", sigs); + //Now some low level checks.. + TxPoWTreeNode tip = MinimaDB.getDB().getTxPoWTree().getTip(); + + //Create a TxPoW.. + TxPoW temp = TxPoWGenerator.generateTxPoW(txn, wit); + + //Redo any checks.. + txn.clearIsMonotonic(); + + boolean validbasic = TxPoWChecker.checkTxPoWBasic(temp); + boolean validsig = TxPoWChecker.checkSignatures(temp); + boolean validmmr = TxPoWChecker.checkMMR(tip.getMMR(), temp); + boolean validscripts = TxPoWChecker.checkTxPoWScripts(tip.getMMR(), temp, tip.getTxPoW()); + + JSONObject valid = new JSONObject(); + valid.put("basic", validbasic); + valid.put("signatures", validsig); + valid.put("mmrproofs", validmmr); + valid.put("scripts", validscripts); + + details.put("valid", valid); + + JSONObject resp = new JSONObject(); ret.put("response", details); diff --git a/src/org/minima/system/commands/txn/txnclear.java b/src/org/minima/system/commands/txn/txnclear.java index 8fc80a635..fc7209401 100644 --- a/src/org/minima/system/commands/txn/txnclear.java +++ b/src/org/minima/system/commands/txn/txnclear.java @@ -10,7 +10,7 @@ public class txnclear extends Command { public txnclear() { - super("txnclear","[id:] - Clear the Witness data"); + super("txnclear","[id:] - Clear ALL the Witness data"); } @Override @@ -27,6 +27,7 @@ public JSONObject runCommand() throws Exception { throw new CommandException("Transaction not found : "+id); } + txnrow.getTransaction().clearIsMonotonic(); txnrow.clearWitness(); JSONObject resp = new JSONObject(); diff --git a/src/org/minima/system/commands/txn/txnexport.java b/src/org/minima/system/commands/txn/txnexport.java index b97891643..65f0ae739 100644 --- a/src/org/minima/system/commands/txn/txnexport.java +++ b/src/org/minima/system/commands/txn/txnexport.java @@ -5,6 +5,7 @@ import org.minima.database.MinimaDB; import org.minima.database.userprefs.txndb.TxnDB; import org.minima.database.userprefs.txndb.TxnRow; +import org.minima.objects.base.MiniData; import org.minima.system.commands.Command; import org.minima.system.commands.CommandException; import org.minima.utils.MiniFile; @@ -14,7 +15,7 @@ public class txnexport extends Command { public txnexport() { - super("txnexport","[id:] [file:] - Export a transaction to a file"); + super("txnexport","[id:] (file:) - Export a transaction as HEX or to a file"); } @Override @@ -24,7 +25,6 @@ public JSONObject runCommand() throws Exception { TxnDB db = MinimaDB.getDB().getCustomTxnDB(); String id = getParam("id"); - String file = getParam("file"); //Get the Transaction.. TxnRow txnrow = db.getTransactionRow(getParam("id")); @@ -32,21 +32,34 @@ public JSONObject runCommand() throws Exception { throw new CommandException("Transaction not found : "+id); } - //Create the file - File output = new File(file); - if(output.exists()) { - output.delete(); + if(existsParam("file")) { + //The File.. + String file = getParam("file"); + + //Create the file + File output = new File(file); + if(output.exists()) { + output.delete(); + } + + //Now export this to a file.. + MiniFile.writeObjectToFile(output, txnrow); + + JSONObject resp = new JSONObject(); + resp.put("file", output.getAbsolutePath()); + resp.put("size", MiniFormat.formatSize(output.length())); + + ret.put("response", resp); + }else { + + //Output to HEX.. + MiniData dv = MiniData.getMiniDataVersion(txnrow); + + JSONObject resp = new JSONObject(); + resp.put("data", dv.to0xString()); + ret.put("response", resp); } - //Now export this to a file.. - MiniFile.writeObjectToFile(output, txnrow); - - JSONObject resp = new JSONObject(); - resp.put("file", output.getAbsolutePath()); - resp.put("size", MiniFormat.formatSize(output.length())); - - ret.put("response", resp); - return ret; } diff --git a/src/org/minima/system/commands/txn/txnimport.java b/src/org/minima/system/commands/txn/txnimport.java index 30795cc61..7a52a8300 100644 --- a/src/org/minima/system/commands/txn/txnimport.java +++ b/src/org/minima/system/commands/txn/txnimport.java @@ -14,7 +14,7 @@ public class txnimport extends Command { public txnimport() { - super("txnimport","[file:] (id:) - Import a transaction. Optionally specify the ID"); + super("txnimport","(id:) (file:) (data:) - Import a transaction as a file or HEX data. Optionally specify the ID"); } @Override @@ -23,29 +23,51 @@ public JSONObject runCommand() throws Exception { TxnDB db = MinimaDB.getDB().getCustomTxnDB(); - String file = getParam("file"); - File ff = new File(file); - if(!ff.exists()) { - throw new CommandException("File does not exist : "+ff.getAbsolutePath()); + if(existsParam("file")) { + String file = getParam("file"); + File ff = new File(file); + if(!ff.exists()) { + throw new CommandException("File does not exist : "+ff.getAbsolutePath()); + } + + //Load it in.. + byte[] txndata = MiniFile.readCompleteFile(ff); + + //Convert to MiniData + MiniData minitxn = new MiniData(txndata); + + //Convert this.. + TxnRow txnrow = TxnRow.convertMiniDataVersion(minitxn); + if(existsParam("id")) { + txnrow.setID(getParam("id")); + } + + db.addCompleteTransaction(txnrow); + + JSONObject resp = new JSONObject(); + ret.put("response", txnrow.toJSON()); + + }else if(existsParam("data")){ + + //Get the HEX data + MiniData dv = getDataParam("data"); + + //Convert to a TxnRow + TxnRow tx = TxnRow.convertMiniDataVersion(dv); + if(existsParam("id")) { + tx.setID(getParam("id")); + } + + //Add to the DB + db.addCompleteTransaction(tx); + + JSONObject resp = new JSONObject(); + ret.put("response", tx.toJSON()); + + }else { + throw new CommandException("Must specify file or data"); } - //Load it in.. - byte[] txndata = MiniFile.readCompleteFile(ff); - - //Convert to MiniData - MiniData minitxn = new MiniData(txndata); - - //Convert this.. - TxnRow txnrow = TxnRow.convertMiniDataVersion(minitxn); - if(existsParam("id")) { - txnrow.setID(getParam("id")); - } - - db.addCompleteTransaction(txnrow); - - JSONObject resp = new JSONObject(); - ret.put("response", txnrow.toJSON()); - return ret; } diff --git a/src/org/minima/system/commands/txn/txninput.java b/src/org/minima/system/commands/txn/txninput.java index bc62ee14c..64707aa6a 100644 --- a/src/org/minima/system/commands/txn/txninput.java +++ b/src/org/minima/system/commands/txn/txninput.java @@ -7,6 +7,7 @@ import org.minima.objects.Transaction; import org.minima.objects.base.MiniData; import org.minima.objects.base.MiniNumber; +import org.minima.system.brains.TxPoWGenerator; import org.minima.system.brains.TxPoWSearcher; import org.minima.system.commands.Command; import org.minima.system.commands.CommandException; @@ -15,7 +16,7 @@ public class txninput extends Command { public txninput() { - super("txninput","[id:] (coinid:) (coindata:) (floating:true|false) (address:) (amount:) (tokenid:) - Add a coin as an input to a transaction"); + super("txninput","[id:] (coinid:) (coindata:) (floating:) (address:) (amount:) (tokenid:) (sciptmmr:true)- Add a coin as an input to a transaction"); } @Override @@ -48,11 +49,6 @@ public JSONObject runCommand() throws Exception { //Is it a floating input.. if(eltoo) { - //Check this coin is floating.. - if(!cc.isFloating()) { - throw new CommandException("Coin cannot be ELTOO as is not floating.. "+cc.toJSON().toString()); - } - cc.resetCoinID(Coin.COINID_ELTOO); } @@ -68,36 +64,36 @@ public JSONObject runCommand() throws Exception { //Is it a floating input.. if(eltoo) { - //Check this coin is floating.. - if(!cc.isFloating()) { - throw new CommandException("Coin cannot be ELTOO as is not floating.. "+cc.toJSON().toString()); - } - cc.resetCoinID(Coin.COINID_ELTOO); } }else { - //Is it a floater.. - if(!eltoo) { - throw new CommandException("Coin MUST be floating if no coinid or coindata specified"); - } - //Get the details.. - String address = getParam("address"); + String address = getAddressParam("address"); String amount = getParam("amount"); String tokenid = getParam("tokenid","0x00"); //Create a COIN.. - cc = new Coin(new MiniData(address), new MiniNumber(amount), new MiniData(tokenid)); - cc.setFloating(true); - cc.resetCoinID(Coin.COINID_ELTOO); - + cc = new Coin(Coin.COINID_ELTOO, new MiniData(address), new MiniNumber(amount), new MiniData(tokenid)); } //Get the transaction.. Transaction trans = txnrow.getTransaction(); trans.addInput(cc); + //Calculate the correct CoinID - if possible.. + TxPoWGenerator.precomputeTransactionCoinID(trans); + + //Calculate transid + trans.calculateTransactionID(); + + //Are we adding the scripts and MMR for this coin.. + boolean smmr = getBooleanParam("scriptmmr", false); + if(smmr) { + //Add the details for this coin.. + txnutils.setMMRandScripts(cc, txnrow.getWitness()); + } + //Output the current trans.. ret.put("response", db.getTransactionRow(id).toJSON()); diff --git a/src/org/minima/system/commands/txn/txnoutput.java b/src/org/minima/system/commands/txn/txnoutput.java index 677f6c4db..e9dd27bc4 100644 --- a/src/org/minima/system/commands/txn/txnoutput.java +++ b/src/org/minima/system/commands/txn/txnoutput.java @@ -8,6 +8,7 @@ import org.minima.objects.Transaction; import org.minima.objects.base.MiniData; import org.minima.objects.base.MiniNumber; +import org.minima.system.brains.TxPoWGenerator; import org.minima.system.brains.TxPoWSearcher; import org.minima.system.commands.Command; import org.minima.system.commands.CommandException; @@ -16,7 +17,7 @@ public class txnoutput extends Command { public txnoutput() { - super("txnoutput","[id:] [amount:] [address:] (tokenid:) (storestate:) (floating:true|false) - Create a transaction output"); + super("txnoutput","[id:] [amount:] [address:] (tokenid:) (storestate:) - Create a transaction output"); } @Override @@ -28,19 +29,21 @@ public JSONObject runCommand() throws Exception { //The transaction String id = getParam("id"); MiniNumber amount = getNumberParam("amount"); - MiniData address = getDataParam("address"); + MiniData address = new MiniData(getAddressParam("address")); boolean storestate = getBooleanParam("storestate", true); - boolean floating = getBooleanParam("floating", false); //Could be a token.. MiniData tokenid = Token.TOKENID_MINIMA; Token token = null; if(existsParam("tokenid")) { tokenid = getDataParam("tokenid"); - token = TxPoWSearcher.getToken(tokenid); - if(token == null) { - throw new CommandException("Token not found : "+tokenid); + //Is it Minima.. + if(!tokenid.isEqual(Token.TOKENID_MINIMA)) { + token = TxPoWSearcher.getToken(tokenid); + if(token == null) { + throw new CommandException("Token not found : "+tokenid); + } } } @@ -51,14 +54,11 @@ public JSONObject runCommand() throws Exception { } //Create the Coin.. - Coin output = new Coin(Coin.COINID_OUTPUT, address, miniamount, tokenid,false,storestate); + Coin output = new Coin(Coin.COINID_OUTPUT, address, miniamount, tokenid,storestate); if(token != null) { output.setToken(token); } - //Is this a floating coin.. - output.setFloating(floating); - //Get the Transaction TxnRow txnrow = db.getTransactionRow(getParam("id")); if(txnrow == null) { @@ -67,6 +67,12 @@ public JSONObject runCommand() throws Exception { Transaction trans = txnrow.getTransaction(); trans.addOutput(output); + //Compute the correct CoinID + TxPoWGenerator.precomputeTransactionCoinID(trans); + + //Calculate transid + trans.calculateTransactionID(); + //Output the current trans.. ret.put("response", db.getTransactionRow(id).toJSON()); diff --git a/src/org/minima/system/commands/txn/txnpost.java b/src/org/minima/system/commands/txn/txnpost.java index e502d8a14..92784ce52 100644 --- a/src/org/minima/system/commands/txn/txnpost.java +++ b/src/org/minima/system/commands/txn/txnpost.java @@ -1,11 +1,15 @@ package org.minima.system.commands.txn; +import java.util.ArrayList; + import org.minima.database.MinimaDB; import org.minima.database.userprefs.txndb.TxnDB; import org.minima.database.userprefs.txndb.TxnRow; +import org.minima.objects.CoinProof; import org.minima.objects.Transaction; import org.minima.objects.TxPoW; import org.minima.objects.Witness; +import org.minima.objects.base.MiniNumber; import org.minima.system.Main; import org.minima.system.brains.TxPoWGenerator; import org.minima.system.commands.Command; @@ -15,7 +19,7 @@ public class txnpost extends Command { public txnpost() { - super("txnpost","[id:] - Post a transaction"); + super("txnpost","[id:] (auto:true) (burn:) - Post a transaction. Automatically set the Scripts and MMR"); } @Override @@ -25,7 +29,11 @@ public JSONObject runCommand() throws Exception { TxnDB db = MinimaDB.getDB().getCustomTxnDB(); //The transaction - String id = getParam("id"); + String id = getParam("id"); + MiniNumber burn = getNumberParam("burn", MiniNumber.ZERO); + if(burn.isLess(MiniNumber.ZERO)) { + throw new CommandException("Cannot have negative burn "+burn.toString()); + } //Get the row.. TxnRow txnrow = db.getTransactionRow(id); @@ -37,11 +45,45 @@ public JSONObject runCommand() throws Exception { Transaction trans = txnrow.getTransaction(); Witness wit = txnrow.getWitness(); - //Set the MMR data and Scripts - txnutils.setMMRandScripts(trans, wit); + //Clear any previous checks.. + txnrow.getTransaction().clearIsMonotonic(); + + //Set the scripts and MMR + boolean auto = getBooleanParam("auto", false); + if(auto) { + //Set the MMR data and Scripts + txnutils.setMMRandScripts(trans, wit); + } + + //Compute the correct CoinID + TxPoWGenerator.precomputeTransactionCoinID(trans); - //Now create the TxPoW - TxPoW txpow = TxPoWGenerator.generateTxPoW(trans, wit); + //Calculate the TransactionID.. + trans.calculateTransactionID(); + + //The final TxPoW + TxPoW txpow = null; + + //Is there a burn + if(burn.isMore(MiniNumber.ZERO)) { + + //Get all the used coins.. + ArrayList addedcoinid = new ArrayList<>(); + ArrayList coins = wit.getAllCoinProofs(); + for(CoinProof cp : coins) { + addedcoinid.add(cp.getCoin().getCoinID().to0xString()); + } + + //Create a Burn Transaction + TxnRow burntxn = txnutils.createBurnTransaction(addedcoinid,trans.getTransactionID(),burn); + + //Now create a complete TxPOW + txpow = TxPoWGenerator.generateTxPoW(trans, wit, burntxn.getTransaction(), burntxn.getWitness()); + + }else { + //Now create the TxPoW + txpow = TxPoWGenerator.generateTxPoW(trans, wit); + } //Calculate the size.. txpow.calculateTXPOWID(); diff --git a/src/org/minima/system/commands/txn/txnsign.java b/src/org/minima/system/commands/txn/txnsign.java index 0c9ac3eb1..122289582 100644 --- a/src/org/minima/system/commands/txn/txnsign.java +++ b/src/org/minima/system/commands/txn/txnsign.java @@ -10,11 +10,11 @@ import org.minima.objects.Coin; import org.minima.objects.Transaction; import org.minima.objects.Witness; -import org.minima.objects.base.MiniData; import org.minima.objects.keys.Signature; +import org.minima.system.brains.TxPoWGenerator; import org.minima.system.commands.Command; import org.minima.system.commands.CommandException; -import org.minima.utils.Crypto; +import org.minima.utils.json.JSONArray; import org.minima.utils.json.JSONObject; public class txnsign extends Command { @@ -41,12 +41,16 @@ public JSONObject runCommand() throws Exception { Transaction txn = txnrow.getTransaction(); Witness wit = txnrow.getWitness(); + //Precompute the CoinID + TxPoWGenerator.precomputeTransactionCoinID(txn); + //Calculate the TransactionID.. - MiniData transid = Crypto.getInstance().hashObject(txn); - + txn.calculateTransactionID(); + //Get the Wallet Wallet walletdb = MinimaDB.getDB().getWallet(); + JSONArray foundkeys = new JSONArray(); //Are we auto signing.. if all the coin inputs are simple if(pubk.equals("auto")) { @@ -55,17 +59,18 @@ public JSONObject runCommand() throws Exception { KeyRow keyrow = walletdb.getKeysRowFromAddress(cc.getAddress().to0xString()); if(keyrow == null) { - txnrow.clearWitness(); - throw new CommandException("ERROR : Script not found for address : "+cc.getAddress().to0xString()); - + continue; + //Is it a simple row.. }else if(keyrow.getPublicKey().equals("")) { - txnrow.clearWitness(); - throw new CommandException("NON-Simple coin found at coin : "+cc.getAddress().to0xString()); + continue; } + //Add to our list + foundkeys.add(keyrow.getPublicKey()); + //Now sign with that.. - Signature signature = walletdb.sign(keyrow.getPrivateKey(), transid); + Signature signature = walletdb.sign(keyrow.getPrivateKey(), txn.getTransactionID()); //Add it.. wit.addSignature(signature); @@ -78,15 +83,19 @@ public JSONObject runCommand() throws Exception { throw new CommandException("Public Key not found : "+pubk); } + foundkeys.add(pubrow.getPublicKey()); + //Use the wallet.. - Signature signature = walletdb.sign(pubrow.getPrivateKey(), transid); + Signature signature = walletdb.sign(pubrow.getPrivateKey(), txn.getTransactionID()); //Add it.. wit.addSignature(signature); } JSONObject resp = new JSONObject(); - ret.put("response", txnrow.toJSON()); + resp.put("keys", foundkeys); + + ret.put("response", resp); return ret; } diff --git a/src/org/minima/system/commands/txn/txnstate.java b/src/org/minima/system/commands/txn/txnstate.java index bfeb9d56f..8999ec142 100644 --- a/src/org/minima/system/commands/txn/txnstate.java +++ b/src/org/minima/system/commands/txn/txnstate.java @@ -43,6 +43,9 @@ public JSONObject runCommand() throws Exception { //Add it to the transaction trans.addStateVariable(sv); + //Calculate transid + trans.calculateTransactionID(); + //Output the current trans.. ret.put("response", db.getTransactionRow(id).toJSON()); diff --git a/src/org/minima/system/commands/txn/txnutils.java b/src/org/minima/system/commands/txn/txnutils.java index 14bd6d9ce..3c75a3bde 100644 --- a/src/org/minima/system/commands/txn/txnutils.java +++ b/src/org/minima/system/commands/txn/txnutils.java @@ -4,15 +4,23 @@ import org.minima.database.MinimaDB; import org.minima.database.mmr.MMRProof; +import org.minima.database.txpowdb.TxPoWDB; import org.minima.database.txpowtree.TxPoWTreeNode; +import org.minima.database.userprefs.txndb.TxnRow; import org.minima.database.wallet.KeyRow; import org.minima.database.wallet.Wallet; import org.minima.objects.Coin; import org.minima.objects.CoinProof; import org.minima.objects.ScriptProof; +import org.minima.objects.Token; import org.minima.objects.Transaction; import org.minima.objects.Witness; +import org.minima.objects.base.MiniData; import org.minima.objects.base.MiniNumber; +import org.minima.objects.keys.Signature; +import org.minima.system.Main; +import org.minima.system.brains.TxPoWGenerator; +import org.minima.system.brains.TxPoWMiner; import org.minima.system.brains.TxPoWSearcher; import org.minima.system.commands.CommandException; import org.minima.system.params.GlobalParams; @@ -20,6 +28,10 @@ public class txnutils { public static void setMMRandScripts(Transaction zTransaction, Witness zWitness) throws Exception { + setMMRandScripts(zTransaction, zWitness, true); + } + + public static void setMMRandScripts(Transaction zTransaction, Witness zWitness, boolean zExitOnFail) throws Exception { //get the tip.. TxPoWTreeNode tip = MinimaDB.getDB().getTxPoWTree().getTip(); @@ -29,27 +41,31 @@ public static void setMMRandScripts(Transaction zTransaction, Witness zWitness) //Are any of the inputs floating ArrayList inputs = new ArrayList<>(); for(Coin cc : baseinputs) { - if(cc.isFloating() && cc.getCoinID().isEqual(Coin.COINID_ELTOO)) { + if(cc.getCoinID().isEqual(Coin.COINID_ELTOO)) { //Get the MOST recent coin to attach to this transaction.. Coin floater = TxPoWSearcher.getFloatingCoin(tip, cc.getAmount(), cc.getAddress(), cc.getTokenID()); if(floater == null) { - throw new CommandException("Could not find valid unspent coin for "+cc.toJSON()); + if(zExitOnFail) { + throw new CommandException("Could not find valid unspent coin for "+cc.toJSON()); + } + }else { + inputs.add(floater); } - inputs.add(floater); - }else { //Get the complete coin given the CoinID //could be a pre-made coin.. so use correct MMREntry / Created Coin current = TxPoWSearcher.searchCoin(cc.getCoinID()); if(current == null) { - throw new CommandException("Coin with CoinID not found : "+cc.getCoinID().to0xString()); + if(zExitOnFail) { + throw new CommandException("Coin with CoinID not found : "+cc.getCoinID().to0xString()); + } + }else { + inputs.add(current); } - - inputs.add(current); } } @@ -81,9 +97,6 @@ public static void setMMRandScripts(Transaction zTransaction, Witness zWitness) //Get the main Wallet Wallet walletdb = MinimaDB.getDB().getWallet(); - //Clear the MMR.. - zWitness.clearCoinProofs(); - //Add the MMR proofs for the coins.. for(Coin input : inputs) { @@ -100,97 +113,232 @@ public static void setMMRandScripts(Transaction zTransaction, Witness zWitness) String scraddress = input.getAddress().to0xString(); KeyRow keyrow = walletdb.getKeysRowFromAddress(scraddress); if(keyrow == null) { - throw new Exception("SERIOUS ERROR script missing for simple address : "+scraddress); + if(zExitOnFail) { + throw new Exception("SERIOUS ERROR script missing for simple address : "+scraddress); + } + }else { + ScriptProof pscr = new ScriptProof(keyrow.getScript()); + zWitness.addScript(pscr); } - - ScriptProof pscr = new ScriptProof(keyrow.getScript()); - zWitness.addScript(pscr); } } + public static void setMMRandScripts(Coin zCoin, Witness zWitness) throws Exception { + //get the tip.. + TxPoWTreeNode tip = MinimaDB.getDB().getTxPoWTree().getTip(); + + //Min depth of a coin + MiniNumber minblock = MiniNumber.ZERO; + + //How deep + if(zCoin.getBlockCreated().isMore(minblock)) { + minblock = zCoin.getBlockCreated(); + } + + //Get the block.. + MiniNumber currentblock = tip.getBlockNumber(); + MiniNumber blockdiff = currentblock.sub(minblock); + if(blockdiff.isMore(GlobalParams.MINIMA_MMR_PROOF_HISTORY)) { + blockdiff = GlobalParams.MINIMA_MMR_PROOF_HISTORY; + } + + //Now get that Block + TxPoWTreeNode mmrnode = tip.getPastNode(tip.getBlockNumber().sub(blockdiff)); + if(mmrnode == null) { + //Not enough blocks.. + throw new Exception("Not enough blocks in chain to make valid MMR Proofs.."); + } + + //Get the main Wallet + Wallet walletdb = MinimaDB.getDB().getWallet(); + + //Get the proof.. + MMRProof proof = mmrnode.getMMR().getProofToPeak(zCoin.getMMREntryNumber()); + + //Create the CoinProof.. + CoinProof cp = new CoinProof(zCoin, proof); + + //Add it to the witness data + zWitness.addCoinProof(cp); + + //Add the script proofs + String scraddress = zCoin.getAddress().to0xString(); + KeyRow keyrow = walletdb.getKeysRowFromAddress(scraddress); + if(keyrow == null) { + throw new Exception("SERIOUS ERROR script missing for simple address : "+scraddress); + } + + ScriptProof pscr = new ScriptProof(keyrow.getScript()); + zWitness.addScript(pscr); + } -// public static Witness createWitness(Transaction zTransaction) throws Exception { -// Witness witness = new Witness(); -// -// //get the tip.. -// TxPoWTreeNode tip = MinimaDB.getDB().getTxPoWTree().getTip(); -// -// //Get all the input coins.. -// ArrayList inputs = zTransaction.getAllInputs(); -// -// //Min depth of a coin -// MiniNumber minblock = MiniNumber.ZERO; -// -// //Add the inputs.. -// for(Coin input : inputs) { -// //How deep -// if(input.getBlockCreated().isMore(minblock)) { -// minblock = input.getBlockCreated(); -// } -// } -// -// //Get the block.. -// MiniNumber currentblock = tip.getBlockNumber(); -// MiniNumber blockdiff = currentblock.sub(minblock); -// if(blockdiff.isMore(GlobalParams.MINIMA_MMR_PROOF_HISTORY)) { -// blockdiff = GlobalParams.MINIMA_MMR_PROOF_HISTORY; -// } -// -// //Now get that Block -// TxPoWTreeNode mmrnode = tip.getPastNode(tip.getBlockNumber().sub(blockdiff)); -// if(mmrnode == null) { -// //Not enough blocks.. -// throw new Exception("Not enough blocks in chain to make valid MMR Proofs.."); -// } -// -// //Get the main Wallet -// Wallet walletdb = MinimaDB.getDB().getWallet(); -// -// //Create a list of the required signatures -// ArrayList reqsigs = new ArrayList<>(); -// -// //Add the MMR proofs for the coins.. -// for(Coin input : inputs) { -// -// //Get the proof.. -// MMRProof proof = mmrnode.getMMR().getProofToPeak(input.getMMREntryNumber()); -// -// //Create the CoinProof.. -// CoinProof cp = new CoinProof(input, proof); -// -// //Add it to the witness data -// witness.addCoinProof(cp); -// -// //Add the script proofs -// String scraddress = input.getAddress().to0xString(); -// KeyRow keyrow = walletdb.getKeysRowFromAddress(scraddress); -// if(keyrow == null) { -// throw new Exception("SERIOUS ERROR script missing for simple address : "+scraddress); -// } -// -// ScriptProof pscr = new ScriptProof(keyrow.getScript()); -// witness.addScript(pscr); -// -// //Add this address to the list we need to sign as.. -// String priv = keyrow.getPrivateKey(); -// if(!reqsigs.contains(priv)) { -// reqsigs.add(priv); -// } -// } -// -// //Calculate the TransactionID.. -// MiniData transid = Crypto.getInstance().hashObject(zTransaction); -// -// //Now that we have constructed the transaction - lets sign it.. -// for(String priv : reqsigs) { -// -// //Use the wallet.. -// Signature signature = walletdb.sign(priv, transid); -// -// //Add it.. -// witness.addSignature(signature); -// } -// -// return witness; -// } + public static TxnRow createBurnTransaction(ArrayList zExcludeCoins, MiniData zLinkTransactionID, MiniNumber zAmount) throws CommandException { + + //The Full Txn.. + TxnRow txnrow = new TxnRow("temp", new Transaction(), new Witness()); + + //Get the DBs + TxPoWDB txpdb = MinimaDB.getDB().getTxPoWDB(); + TxPoWMiner txminer = Main.getInstance().getTxPoWMiner(); + Wallet walletdb = MinimaDB.getDB().getWallet(); + TxPoWTreeNode tip = MinimaDB.getDB().getTxPoWTree().getTip(); + + //How much are we sending.. What are we Burning.. + MiniNumber sendamount = zAmount; + + //Lets build a transaction.. + ArrayList relcoins = TxPoWSearcher.getRelevantUnspentCoins(tip,"0x00",true); + + //The current total + MiniNumber currentamount = MiniNumber.ZERO; + ArrayList currentcoins = new ArrayList<>(); + + //Now cycle through.. + for(Coin coin : relcoins) { + + String coinidstr = coin.getCoinID().to0xString(); + + //Is it to be excluded.. + if(zExcludeCoins.contains(coinidstr)) { + continue; + } + + //Check if we are already using thewm in another Transaction that is being mined + if(txminer.checkForMiningCoin(coinidstr)) { + continue; + } + + //Check if in mempool.. + if(txpdb.checkMempoolCoins(coin.getCoinID())) { + continue; + } + + //Add this coin.. + currentcoins.add(coin); + + //Get the actual ammount.. + currentamount = currentamount.add(coin.getAmount()); + + //Do we have enough.. + if(currentamount.isMoreEqual(sendamount)) { + break; + } + } + + //Did we add enough + if(currentamount.isLess(sendamount)) { + //Not enough funds.. + throw new CommandException("Not enough funds / coins for the burn.."); + } + + //What is the change.. + MiniNumber change = currentamount.sub(sendamount); + + //Lets construct a txn.. + Transaction transaction = txnrow.getTransaction(); + Witness witness = txnrow.getWitness(); + + //Min depth of a coin + MiniNumber minblock = MiniNumber.ZERO; + + //Add the inputs.. + for(Coin inputs : currentcoins) { + + //Add this input to our transaction + transaction.addInput(inputs); + + //How deep + if(inputs.getBlockCreated().isMore(minblock)) { + minblock = inputs.getBlockCreated(); + } + } + + //Get the block.. + MiniNumber currentblock = tip.getBlockNumber(); + MiniNumber blockdiff = currentblock.sub(minblock); + if(blockdiff.isMore(GlobalParams.MINIMA_MMR_PROOF_HISTORY)) { + blockdiff = GlobalParams.MINIMA_MMR_PROOF_HISTORY; + } + + //Now get that Block + TxPoWTreeNode mmrnode = tip.getPastNode(tip.getBlockNumber().sub(blockdiff)); + if(mmrnode == null) { + //Not enough blocks.. + throw new CommandException("Not enough blocks in chain to make valid MMR Proofs.."); + } + + //Create a list of the required signatures + ArrayList reqsigs = new ArrayList<>(); + + //Add the MMR proofs for the coins.. + for(Coin input : currentcoins) { + + //Get the proof.. + MMRProof proof = mmrnode.getMMR().getProofToPeak(input.getMMREntryNumber()); + + //Create the CoinProof.. + CoinProof cp = new CoinProof(input, proof); + + //Add it to the witness data + witness.addCoinProof(cp); + + //Add the script proofs + String scraddress = input.getAddress().to0xString(); + KeyRow keyrow = walletdb.getKeysRowFromAddress(scraddress); + if(keyrow == null) { + throw new CommandException("SERIOUS ERROR script missing for simple address : "+scraddress); + } + + ScriptProof pscr = new ScriptProof(keyrow.getScript()); + witness.addScript(pscr); + + //Add this address to the list we need to sign as.. + String priv = keyrow.getPrivateKey(); + if(!reqsigs.contains(priv)) { + reqsigs.add(priv); + } + } + + //Check valid - for Minima.. + if(!sendamount.isValidMinimaValue()) { + throw new CommandException("Invalid Minima amount to send.. "+sendamount.toString()); + } + + //Do we need to send change.. + if(change.isMore(MiniNumber.ZERO)) { + //Create a new address + KeyRow newwalletaddress = MinimaDB.getDB().getWallet().getDefaultKeyAddress(); + MiniData chgaddress = new MiniData(newwalletaddress.getAddress()); + + //Get the scaled token ammount.. + MiniNumber changeamount = change; + + //Change coin does not keep the state + Coin changecoin = new Coin(Coin.COINID_OUTPUT, chgaddress, changeamount, Token.TOKENID_MINIMA, false); + + //And finally.. add the change output + transaction.addOutput(changecoin); + } + + //Set the Link Hash! - as this is a BURN transaction + transaction.setLinkHash(zLinkTransactionID); + + //Compute the correct CoinID + TxPoWGenerator.precomputeTransactionCoinID(transaction); + + //Calculate the TransactionID.. + transaction.calculateTransactionID(); + + //Now that we have constructed the transaction - lets sign it.. + for(String priv : reqsigs) { + + //Use the wallet.. + Signature signature = walletdb.sign(priv, transaction.getTransactionID()); + + //Add it.. + witness.addSignature(signature); + } + + return txnrow; + } } diff --git a/src/org/minima/system/network/NetworkManager.java b/src/org/minima/system/network/NetworkManager.java index 73dff308a..8aa6583f6 100644 --- a/src/org/minima/system/network/NetworkManager.java +++ b/src/org/minima/system/network/NetworkManager.java @@ -206,7 +206,9 @@ public void shutdownNetwork() { public boolean isShutDownComplete() { return mNIOManager.isShutdownComplete() - && mP2PManager.isShutdownComplete(); + && mSSHManager.isShutdownComplete() + && mP2PManager.isShutdownComplete() + && mNotifyManager.isShutdownComplete(); } public MessageProcessor getP2PManager() { diff --git a/src/org/minima/system/network/minima/NIOManager.java b/src/org/minima/system/network/minima/NIOManager.java index f450ce865..d48d9ee60 100644 --- a/src/org/minima/system/network/minima/NIOManager.java +++ b/src/org/minima/system/network/minima/NIOManager.java @@ -221,6 +221,11 @@ protected void processMessage(Message zMessage) throws Exception { //Shut down the NIO mNIOServer.shutdown(); + //Wait for it to stop.. + while(mNIOServer.isRunning()) { + Thread.sleep(50); + } + //Stop this.. stopMessageProcessor(); @@ -512,18 +517,6 @@ public void disconnect(String zClientUID) { Message msg = new Message(NIOManager.NIO_DISCONNECT).addString("uid", zClientUID); PostMessage(msg); } - - /** - * Small delay before actually posting the request.. 2 second.. - */ - public static void sendDelayedTxPoWReq(String zClientID, String zTxPoWID, String zReason) { - TimerMessage timed = new TimerMessage(2000, NIO_TXPOWREQ); - timed.addString("client", zClientID); - timed.addString("txpowid", zTxPoWID); - timed.addString("reason",zReason); - - Main.getInstance().getNIOManager().PostTimerMessage(timed); - } /** * Send network messages diff --git a/src/org/minima/system/network/minima/NIOMessage.java b/src/org/minima/system/network/minima/NIOMessage.java index 5dc0bfafa..2bec460c1 100644 --- a/src/org/minima/system/network/minima/NIOMessage.java +++ b/src/org/minima/system/network/minima/NIOMessage.java @@ -2,10 +2,12 @@ import java.io.ByteArrayInputStream; import java.io.DataInputStream; +import java.math.BigDecimal; +import java.math.MathContext; import java.util.ArrayList; +import java.util.Date; import org.minima.database.MinimaDB; -import org.minima.database.mmr.MMR; import org.minima.database.txpowdb.TxPoWDB; import org.minima.database.txpowtree.TxPoWTreeNode; import org.minima.objects.Greeting; @@ -132,7 +134,6 @@ public void run() { Greeting greet = Greeting.ReadFromStream(dis); //What version.. - //if(!greet.getVersion().toString().startsWith("TN-P2P.100")) { if(!greet.getVersion().toString().startsWith("0.101")) { MinimaLogger.log("Greeting with Incompatible Version! "+greet.getVersion().toString()); @@ -211,31 +212,7 @@ public void run() { //A small message.. MinimaLogger.log("[+] Connected to the blockchain Initial Block Download received. size:"+MiniFormat.formatSize(data.length)+" blocks:"+ibd.getTxBlocks().size()); - //Do some checking! -// //Sort the Sync blocks - low to high - they should be in the correct order but just in case.. -// Collections.sort(ibd.getSyncBlocks(), new Comparator() { -// @Override -// public int compare(TxBlock o1, TxBlock o2) { -// // TODO Auto-generated method stub -// return 0; -// } -// }); - - //Check all the syncblocks are there with none missng.. - //.. - boolean valid = true; - - //Valid.. - if(ibd.hasCascade() && !ibd.getCascade().checkCascade()) { - MinimaLogger.log("ERROR Invalid Cascade sent from "+mClientUID); - - //Disconnect..Something fishy.. - Main.getInstance().getNIOManager().disconnect(mClientUID); - - return; - } - - //Send to the Processor - with the client in case is invalid.. in which case disconnect + //Send to the Processor Main.getInstance().getTxPoWProcessor().postProcessIBD(ibd, mClientUID); }else if(type.isEqual(MSG_TXPOWID)) { @@ -279,46 +256,68 @@ public void run() { //Now get the current tip details TxPoWTreeNode tip = MinimaDB.getDB().getTxPoWTree().getTip(); TxPoWTreeNode cascade = MinimaDB.getDB().getTxPoWTree().getRoot(); - MMR tipmmr = tip.getMMR(); - TxPoW tiptxpow = tip.getTxPoW(); + + //Have we got any blocks at all yet.. + if(tip == null) { + return; + } //The block and cascade block MiniNumber cascadeblock = cascade.getBlockNumber(); MiniNumber block = txpow.getBlockNumber(); //Check if is a block and within range of our current tip - double blockdiffratio = TxPoWChecker.checkDifficulty(tip.getTxPoW().getBlockDifficulty(), txpow.getBlockDifficulty()); + BigDecimal tipdec = new BigDecimal(tip.getTxPoW().getBlockDifficulty().getDataValue()); + BigDecimal blockdec = new BigDecimal(txpow.getBlockDifficulty().getDataValue()); + double blockdiffratio = tipdec.divide(blockdec, MathContext.DECIMAL32).doubleValue(); - //For BOTH txns and blocks - if(block.isLess(cascadeblock)) { - //Block before cascade - MinimaLogger.log("Received block before cascade.. "+block+" / "+cascadeblock+" difficulty:"+blockdiffratio); - return; - } + //Start a timer.. + long timestart = System.currentTimeMillis(); - if(blockdiffratio < 0.25) { - //Block difficulty too low.. - MinimaLogger.log("Received txpow with block difficulty too low.. "+blockdiffratio+" "+txpow.getBlockNumber()+" "+txpow.getTxPoWID()); - return; - } + //Some BASIC checks that MUST pass.. + boolean disconnectpeer = false; - //OK - Some basic checks.. - if(!TxPoWChecker.checkTxPoWBasic(txpow)) { - //These MUST PASS + //NONE of these should fail + if(!txpow.getChainID().isEqual(TxPoWChecker.CURRENT_NETWORK)) { + //Check ChainID + MinimaLogger.log("Wrong Block ChainID! "+txpow.getChainID()+" "+txpow.getTxPoWID()); + disconnectpeer = true; + + }else if(!TxPoWChecker.checkTxPoWBasic(txpow)) { + //Basic checks for valid TxPoW MinimaLogger.log("TxPoW FAILS Basic checks from "+mClientUID+" "+txpow.getTxPoWID()); - return; - } + disconnectpeer = true; - if(!TxPoWChecker.checkSignatures(txpow)) { + }else if(!TxPoWChecker.checkSignatures(txpow)) { + //Check the Signatures MinimaLogger.log("Invalid signatures on txpow from "+mClientUID+" "+txpow.getTxPoWID()); + disconnectpeer = true; + } + + //Do we disconnect yet.. + if(disconnectpeer) { + Main.getInstance().getNIOManager().disconnect(mClientUID); return; } //More CHECKS.. if ALL these pass will forward otherwise may be a branch txpow that we requested boolean fullyvalid = true; + //Interesting info.. check this.. probably a timing issue + if(blockdiffratio < 0.1) { + //Block difficulty too low.. + MinimaLogger.log("Received txpow with low block difficulty.. "+blockdiffratio+" "+txpow.getBlockNumber()+" "+txpow.getTxPoWID()); + fullyvalid = false; + } + + if(block.isLessEqual(cascadeblock)) { + //Block before cascade + MinimaLogger.log("Received block before cascade.. "+block+" / "+cascadeblock+" difficulty:"+blockdiffratio); + fullyvalid = true; + } + //Check the Scripts - could fail.. - if(!TxPoWChecker.checkTxPoWScripts(tipmmr, txpow, tiptxpow.getBlockNumber())) { + if(!TxPoWChecker.checkTxPoWScripts(tip.getMMR(), txpow, tip.getTxPoW())) { //Monotonic txn MUST pass the script check or is INVALID - since will never pass.. if(txpow.isMonotonic()) { MinimaLogger.log("Error Monotonic TxPoW failed script check from Client:"+mClientUID+" "+txpow.getTxPoWID()); @@ -331,6 +330,15 @@ public void run() { fullyvalid = false; } + //Max time in the future.. 2 hours.. could be OUR clock.. + if(txpow.isBlock()) { + MiniNumber maxtime = new MiniNumber(System.currentTimeMillis() + (1000 * 60 * 120)); + if(txpow.getTimeMilli().isMore(maxtime)) { + MinimaLogger.log("TxPoW block received with millitime MORE than 2 hours in future "+new Date(txpow.getTimeMilli().getAsLong())+" "+txpow.getTxPoWID()); + fullyvalid = false; + } + } + //Check for mempool coins.. if(TxPoWChecker.checkMemPoolCoins(txpow)) { //Same coins in different transaction - could have been requested by us from branch @@ -338,7 +346,15 @@ public void run() { } //Check the MMR - could be in a separate branch - if(!TxPoWChecker.checkMMR(tipmmr, txpow)) { + if(!TxPoWChecker.checkMMR(tip.getMMR(), txpow)) { + fullyvalid = false; + } + + //How long did all that take.. + long timefinish = System.currentTimeMillis(); + long timediff = timefinish - timestart; + if(timediff > 1000) { + MinimaLogger.log("Message took a long time ("+timediff+"ms) to process @ "+txpow.getTxPoWID()); fullyvalid = false; } @@ -347,11 +363,12 @@ public void run() { //Since it's OK.. forward the TxPoWID to the rest of the network.. if(fullyvalid) { - //Forward to the network NIOManager.sendNetworkMessageAll(MSG_TXPOWID, txpow.getTxPoWIDData()); + } - //Check all the Transactions.. + //Check all the Transactions.. if it's a block + if(txpow.isBlock()) { ArrayList txns = txpow.getBlockTransactions(); for(MiniData txn : txns) { exists = MinimaDB.getDB().getTxPoWDB().exists(txn.to0xString()); @@ -361,7 +378,7 @@ public void run() { } } - //Get the parent if we don't have it.. and is infront of the Cascade.. + //Get the parent if we don't have it.. exists = MinimaDB.getDB().getTxPoWDB().exists(txpow.getParentID().to0xString()); if(!exists) { NIOManager.sendNetworkMessage(mClientUID, MSG_TXPOWREQ, txpow.getParentID()); @@ -391,6 +408,10 @@ public void run() { //Get the Client NIOClient nioclient = Main.getInstance().getNIOManager().getNIOServer().getClient(mClientUID); + if(nioclient == null) { + MinimaLogger.log(mClientUID+" Error null client on P2P NIOMessage.."); + return; + } //Is this one of our Maxima Clients / Hosts.. if so ignore all P2P messages.. Maxima max = Main.getInstance().getMaxima(); @@ -464,7 +485,6 @@ public void run() { //Request all the blocks.. in the correct order for(MiniData block : requestlist) { NIOManager.sendNetworkMessage(mClientUID, MSG_TXPOWREQ, block); -// NIOManager.sendDelayedTxPoWReq(mClientUID, block.to0xString(), "PULSE"); } }else{ diff --git a/src/org/minima/system/network/minima/NIOServer.java b/src/org/minima/system/network/minima/NIOServer.java index 18102e540..1cf4d1c85 100644 --- a/src/org/minima/system/network/minima/NIOServer.java +++ b/src/org/minima/system/network/minima/NIOServer.java @@ -16,12 +16,13 @@ import org.minima.objects.base.MiniData; import org.minima.utils.MinimaLogger; import org.minima.utils.messages.Message; +import org.minima.utils.messages.MessageProcessor; public class NIOServer implements Runnable { public static boolean mTraceON = false; - NIOManager mNIOManager; + MessageProcessor mNIOManager; int mPort; @@ -35,7 +36,9 @@ public class NIOServer implements Runnable { ArrayList mDisconnectChannels; - public NIOServer(int zPort, NIOManager zNIOManager) { + boolean mIsRunning = false; + + public NIOServer(int zPort, MessageProcessor zNIOManager) { mPort = zPort; mNIOManager = zNIOManager; mRegisterChannels = new ArrayList<>(); @@ -101,10 +104,17 @@ public void sendMessageAll(MiniData zData) { } } + public boolean isRunning() { + return mIsRunning; + } + @Override public void run() { try { + //We are running + mIsRunning = true; + // Bind to 0.0.0.0 address which is the local network stack InetAddress addr = InetAddress.getByName("0.0.0.0"); @@ -135,11 +145,6 @@ public void run() { // This is the main loop while (!mShutDown) { -// //Logs.. -// if(mTraceON) { -// MinimaLogger.log("[NIOSERVER] Waiting for selection.."); -// } - //Select something.. mSelector.select(30000); @@ -238,17 +243,25 @@ public void run() { } } - //Notify.. - if(mTraceON) { - MinimaLogger.log("[NIOServer] SHUTDOWN"); - } - - //Shut down the socket.. - serversocket.close(); + //Shut down the socket.. + serversocket.close(); + //Disconnect all clients.. + Enumeration clients = mClients.elements(); + while(clients.hasMoreElements()) { + clients.nextElement().disconnect(); + } + + //Need to call this to shut down properly + mSelector.selectNow(); + }catch(Exception exc) { MinimaLogger.log(exc); } + + //Not running anymore.. + MinimaLogger.log("[NIOServer] SHUTDOWN"); + mIsRunning = false; } private void addChannel(boolean zIncoming, SocketChannel zSocketChannel) throws IOException { @@ -291,4 +304,45 @@ private void addChannel(boolean zIncoming, SocketChannel zSocketChannel) throws mNIOManager.PostMessage(newclient); } + public static void main(String[] zArgs) throws Exception { + + NIOServer nio = new NIOServer(9002, new MessageProcessor("hello") { + @Override + protected void processMessage(Message zMessage) throws Exception { + MinimaLogger.log(zMessage.toString()); + } + }); + + Thread tt = new Thread(nio); + tt.start(); + + MinimaLogger.log("Started.. waiting.."); + Thread.sleep(2000); + + MinimaLogger.log("Stopping.."); + nio.shutdown(); + Thread.sleep(2000); + + //Now start again.. + nio = new NIOServer(9002, new MessageProcessor("hello") { + @Override + protected void processMessage(Message zMessage) throws Exception { + MinimaLogger.log(zMessage.toString()); + } + }); + + tt = new Thread(nio); + tt.start(); + + MinimaLogger.log("Started again.. waiting.."); + Thread.sleep(2000); + + MinimaLogger.log("Stopping.."); + nio.shutdown(); + Thread.sleep(2000); + + + System.exit(0); + } + } diff --git a/src/org/minima/system/network/p2p/params/P2PParams.java b/src/org/minima/system/network/p2p/params/P2PParams.java index 4016b8dd7..f12d95d33 100644 --- a/src/org/minima/system/network/p2p/params/P2PParams.java +++ b/src/org/minima/system/network/p2p/params/P2PParams.java @@ -65,20 +65,15 @@ public class P2PParams { public static int METRICS_DELAY = 600_000; - public static String METRICS_URL = "http://35.242.245.96/network"; + public static String METRICS_URL = "http://35.187.82.116/network"; public static List DEFAULT_NODE_LIST = Arrays.asList( - new InetSocketAddress("34.76.220.73", 9001), // minima-tn100-testnet-vm - new InetSocketAddress("34.134.70.105", 9001), // base-testnet-1 - new InetSocketAddress("34.134.70.105", 9001), // base-testnet-2 - new InetSocketAddress("34.88.24.8", 9001), // base-testnet-3 - new InetSocketAddress("104.155.19.103", 9001), // base-testnet-4 - new InetSocketAddress("34.95.47.143", 9001), // base-testnet-5 - new InetSocketAddress("34.130.196.123", 9001), // base-testnet-6 - new InetSocketAddress("34.92.105.237", 9001), // base-testnet-7 - new InetSocketAddress("34.64.175.169", 9001), // base-testnet-8 - new InetSocketAddress("34.93.20.120", 9001) // base-testnet-9 - + new InetSocketAddress("35.189.237.10", 9001), // v101-genesis-node + new InetSocketAddress("34.92.63.200", 9001), // v101-asia-hong-kong + new InetSocketAddress("34.93.179.55", 9001), // v101-asia-india + new InetSocketAddress("34.65.164.213", 9001), // v101-eu-zurich + new InetSocketAddress("34.151.221.133", 9001), // v101-southamerica-east + new InetSocketAddress("34.67.254.187", 9001) // v101-usa-central ); } diff --git a/src/org/minima/system/params/GeneralParams.java b/src/org/minima/system/params/GeneralParams.java index 8ba3ba938..4165a33d8 100644 --- a/src/org/minima/system/params/GeneralParams.java +++ b/src/org/minima/system/params/GeneralParams.java @@ -52,11 +52,6 @@ public class GeneralParams { */ public static int MINIMA_PORT = 9001; - /** - * The Maxima port - */ - public static int MAXIMA_PORT = 9003; - /** * The Minima RPC port */ @@ -90,7 +85,7 @@ public class GeneralParams { /** * How many days do you keep the TxPoW in the SQL DB */ - public static long NUMBER_DAYS_SQLTXPOWDB = 2; + public static long NUMBER_DAYS_SQLTXPOWDB = 3; /** * How Many Hours do you keep the TxPOW in the RAM mempool @@ -100,7 +95,7 @@ public class GeneralParams { /** * How many days do you archive the TxBlocks to resync Users */ - public static long NUMBER_DAYS_ARCHIVE = 28; + public static long NUMBER_DAYS_ARCHIVE = 90; /** * Number of seconds before sending a pulse message - every 10 minutes diff --git a/src/org/minima/system/params/GlobalParams.java b/src/org/minima/system/params/GlobalParams.java index b3426e4e8..15ed58917 100644 --- a/src/org/minima/system/params/GlobalParams.java +++ b/src/org/minima/system/params/GlobalParams.java @@ -7,7 +7,7 @@ public class GlobalParams { /** * Which Version */ - public static String MINIMA_VERSION = "0.100.33.1"; + public static String MINIMA_VERSION = "0.101.0"; /** * Speed in blocks per second.. @@ -45,17 +45,17 @@ public class GlobalParams { */ public static int MINIMA_CASCADE_LEVELS = 32; - /** - * Current default HASH_Strength Used. Can be up to 512. - * All the MINING, TxPoW and MMR data ALWAYS uses 512. But addresses, scripts, and public keys.. - * can be set to less. This way signatures and addresses are shorter. - */ - public static int MINIMA_DEFAULT_HASH_STRENGTH = 256; - /** * Max Proof History - how far back to use a proof of coin.. * If there is a re-org of more than this the proof will be invalid */ public static MiniNumber MINIMA_MMR_PROOF_HISTORY = new MiniNumber(256); - + + /** + * The MEDIAN time block is taken from this many blocks back + * When calculating the Difficulty of a block ( both from the tip and the previous block ) + * This smooths out the time fluctuations for different blocks and removes incorrect times. + */ + public static int MEDIAN_BLOCK_CALC = 32; + } diff --git a/src/org/minima/system/params/TestParams.java b/src/org/minima/system/params/TestParams.java index 244191e80..503ada7e1 100644 --- a/src/org/minima/system/params/TestParams.java +++ b/src/org/minima/system/params/TestParams.java @@ -11,15 +11,16 @@ public class TestParams { /** * Speed in blocks per second.. + * - 0.02 = 50 second block time * - 0.05 = 20 second block time - * - 0.2 = 5 second blocktime + * - 0.2 = 5 second block time */ - public static MiniNumber MINIMA_BLOCK_SPEED = new MiniNumber("0.2"); + public static MiniNumber MINIMA_BLOCK_SPEED = new MiniNumber("0.05"); /** * When checking speed and average difficulty only look at this many blocks back */ - public static MiniNumber MINIMA_BLOCKS_SPEED_CALC = new MiniNumber(8); + public static MiniNumber MINIMA_BLOCKS_SPEED_CALC = new MiniNumber(16); /** * How deep before we think confirmed.. @@ -34,7 +35,7 @@ public class TestParams { /** * Depth before we cascade.. */ - public static MiniNumber MINIMA_CASCADE_START_DEPTH = new MiniNumber(16); + public static MiniNumber MINIMA_CASCADE_START_DEPTH = new MiniNumber(128); /** @@ -47,13 +48,6 @@ public class TestParams { */ public static int MINIMA_CASCADE_LEVELS = 32; - /** - * Current default HASH_Strength Used. Can be up to 512. - * All the MINING, TxPoW and MMR data ALWAYS uses 512. But addresses, scripts, and public keys.. - * can be set to less. This way signatures and addresses are shorter. - */ - public static int MINIMA_DEFAULT_HASH_STRENGTH = 256; - /** * Max Proof History - how far back to use a proof of coin.. * If there is a re-org of more than this the proof will be invalid @@ -71,7 +65,6 @@ public static void setTestParams() { GlobalParams.MINIMA_CASCADE_LEVELS = TestParams.MINIMA_CASCADE_LEVELS; GlobalParams.MINIMA_CASCADE_START_DEPTH = TestParams.MINIMA_CASCADE_START_DEPTH; GlobalParams.MINIMA_CONFIRM_DEPTH = TestParams.MINIMA_CONFIRM_DEPTH; - GlobalParams.MINIMA_DEFAULT_HASH_STRENGTH = TestParams.MINIMA_DEFAULT_HASH_STRENGTH; GlobalParams.MINIMA_MMR_PROOF_HISTORY = TestParams.MINIMA_MMR_PROOF_HISTORY; GlobalParams.MINIMA_VERSION = TestParams.MINIMA_VERSION; } diff --git a/src/org/minima/utils/BaseConverter.java b/src/org/minima/utils/BaseConverter.java index c17d7f947..05fe762b3 100644 --- a/src/org/minima/utils/BaseConverter.java +++ b/src/org/minima/utils/BaseConverter.java @@ -75,110 +75,6 @@ public static int hexToNumber(String zHex) { } - /** - * BASE 32 Encode and Decode - */ -// private static final String HEX32ARRAY = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; -// private static final String HEX32ARRAY = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"; - -// private static int findChar32(char zChar){ -// return HEX32ARRAY.indexOf(zChar); -// } -// -// public static String encode32(byte[] zData) throws ArithmeticException { -// if(zData.length % 5 != 0) { -// throw new ArithmeticException("Minima Address Encode32 data MUST be multiples of 5 in length"); -// } -// -// //The final length -// int rounds = zData.length / 5; -// int len = rounds * 8 ; -// -// //The data32 array -// int[] data32 = new int[len]; -// -// //The return string and temp variables -// String hex32 = ""; -// int counter=0; -// int currByte, digit; -// -// for(int i=0;i> 3; -// digit = (currByte & 7) << 2; -// -// //2 -// currByte = zData[counter++] & 255; -// data32[start+1] = digit | (currByte >> 6); -// data32[start+2] = (currByte >> 1) & 31; -// digit = (currByte & 1) << 4; -// -// //3 -// currByte = zData[counter++] & 255; -// data32[start+3] = digit | (currByte >> 4); -// digit = (currByte & 15) << 1; -// -// //4 -// currByte = zData[counter++] & 255; -// data32[start+4] = digit | (currByte >> 7); -// data32[start+5] = (currByte >> 2) & 31; -// digit = (currByte & 3) << 3; -// -// //5 -// currByte = zData[counter++] & 255; -// data32[start+6] = digit | (currByte >> 5); -// data32[start+7] = currByte & 31; -// } -// -// //Now add to the result string -// for(int i=0;i> 2) & 255); -// redata[sdata+1] = (byte) ((digits[sdigi+1] << 6 | digits[sdigi+2] << 1 | digits[sdigi+3] >> 4) & 255); -// redata[sdata+2] = (byte) ((digits[sdigi+3] << 4 | digits[sdigi+4] >> 1) & 255); -// redata[sdata+3] = (byte) ((digits[sdigi+4] << 7 | digits[sdigi+5] << 2 | digits[sdigi+6] >> 3) & 255); -// redata[sdata+4] = (byte) ((digits[sdigi+6] << 5 | digits[sdigi+7]) & 255); -// } -// -// return redata; -// } - public static String encode32(byte[] zData) throws ArithmeticException { //First create a BigInter diff --git a/src/org/minima/utils/Crypto.java b/src/org/minima/utils/Crypto.java index 55331b57a..567b22ef9 100644 --- a/src/org/minima/utils/Crypto.java +++ b/src/org/minima/utils/Crypto.java @@ -201,12 +201,8 @@ public MiniData hashAllObjects(Streamable... zObjects) { //Get the Data.. ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos); - -// System.out.println("***HASH_ALL_OBJECTS START"); + for(Streamable object : zObjects) { - //Notify.. -// System.out.println(object.toString()+","); - //Write to the stream object.writeDataStream(dos); } diff --git a/src/org/minima/utils/json/JSONObject.java b/src/org/minima/utils/json/JSONObject.java index e2ff02307..776012d81 100644 --- a/src/org/minima/utils/json/JSONObject.java +++ b/src/org/minima/utils/json/JSONObject.java @@ -106,6 +106,7 @@ public String toJSONString(){ return toJSONString(this); } + @Override public String toString(){ return toJSONString(); } diff --git a/src/org/minima/utils/messages/Message.java b/src/org/minima/utils/messages/Message.java index c7b147b45..db5949270 100644 --- a/src/org/minima/utils/messages/Message.java +++ b/src/org/minima/utils/messages/Message.java @@ -137,6 +137,14 @@ public boolean getBoolean(String zName){ return ((Boolean)bool).booleanValue(); } + public boolean getBoolean(String zName, boolean zDefault){ + if(exists(zName)) { + return getBoolean(zName); + } + + return zDefault; + } + /** * Get an Integer value * @param zName diff --git a/test/org/minima/objects/CoinTests.java b/test/org/minima/objects/CoinTests.java index 8c8562470..ab316cf08 100644 --- a/test/org/minima/objects/CoinTests.java +++ b/test/org/minima/objects/CoinTests.java @@ -36,9 +36,6 @@ public void testCoin() { Coin c = new Coin(coinId, coinAddress, two, tokenId); // System.out.println("Coin created values - " + c.toJSON()); - assertFalse("Floating should be false", c.isFloating()); - c.setFloating(true); - assertTrue("Floating should be true", c.isFloating()); // System.out.println("Coin id value - " + c.mCoinID); // c.resetCoinID(coinId2); assertTrue("Token id should equal", c.getTokenID().equals(tokenId)); @@ -60,7 +57,6 @@ public void testReadAndWriteDataStream() { MiniData tokenId = new MiniData("123"); Coin c = new Coin(coinId, coinAddress, twelve, tokenId); - c.setFloating(true); // System.out.println("coin value before write " + c.toString()); ByteArrayOutputStream bos = new ByteArrayOutputStream(); diff --git a/test/org/minima/tests/kissvm/ContractTests.java b/test/org/minima/tests/kissvm/ContractTests.java index 38e970739..0d6289fba 100644 --- a/test/org/minima/tests/kissvm/ContractTests.java +++ b/test/org/minima/tests/kissvm/ContractTests.java @@ -23,6 +23,7 @@ import org.minima.objects.Transaction; import org.minima.objects.Witness; import org.minima.objects.base.MiniData; +import org.minima.utils.MinimaLogger; public class ContractTests { @@ -252,12 +253,12 @@ public void testExecution() { { String Script = ""; - for (int i = 0; i < Contract.MAX_INSTRUCTIONS; i++) { + Contract ctr = new Contract(Script, "", new Witness(), new Transaction(), new ArrayList(), true); + for (int i = 0; i < ctr.MAX_INSTRUCTIONS; i++) { Script = Script + "LET a = " + i + " "; } { - Contract ctr = new Contract(Script, "", new Witness(), new Transaction(), new ArrayList(), true); ctr.run(); assertTrue(ctr.isParseOK()); assertFalse(ctr.isException()); @@ -267,31 +268,33 @@ public void testExecution() { { String Script = ""; - for (int i = 0; i <= Contract.MAX_INSTRUCTIONS; i++) { + Contract ctr = new Contract(Script, "", new Witness(), new Transaction(), new ArrayList(), true); + for (int i = 0; i < ctr.MAX_INSTRUCTIONS+1; i++) { Script = Script + "LET a = " + i + " "; } - + ctr = new Contract(Script, "", new Witness(), new Transaction(), new ArrayList(), true); { - Contract ctr = new Contract(Script, "", new Witness(), new Transaction(), new ArrayList(), false); + MinimaLogger.log("RUN THIS.. "); ctr.run(); assertTrue(ctr.isParseOK()); assertTrue(ctr.isException()); - assertEquals("org.minima.kissvm.exceptions.ExecutionException: MAX instruction number reached! 257", ctr.getException()); + assertEquals("org.minima.kissvm.exceptions.ExecutionException: MAX instruction number reached! 1025", ctr.getException()); } } { String Script = ""; - for (int i = 0; i <= Contract.MAX_INSTRUCTIONS; i++) { + Contract ctr = new Contract(Script, "", new Witness(), new Transaction(), new ArrayList(), true); + for (int i = 0; i < ctr.MAX_INSTRUCTIONS+1; i++) { Script = Script + "LET a = " + i + " "; } - + ctr = new Contract(Script, "", new Witness(), new Transaction(), new ArrayList(), true); + { - Contract ctr = new Contract(Script, "", new Witness(), new Transaction(), new ArrayList(), true); ctr.run(); assertTrue(ctr.isParseOK()); assertTrue(ctr.isException()); - assertEquals("org.minima.kissvm.exceptions.ExecutionException: MAX instruction number reached! 257", ctr.getException()); + assertEquals("org.minima.kissvm.exceptions.ExecutionException: MAX instruction number reached! 1025", ctr.getException()); } } } diff --git a/test/org/minima/tests/kissvm/functions/base/LENTests.java b/test/org/minima/tests/kissvm/functions/base/LENTests.java index f97766fd8..fc0ee31e6 100644 --- a/test/org/minima/tests/kissvm/functions/base/LENTests.java +++ b/test/org/minima/tests/kissvm/functions/base/LENTests.java @@ -59,6 +59,18 @@ public void testValidParams() { fail(); } } + + { + MinimaFunction mf = fn.getNewFunction(); + mf.addParameter(new ConstantExpression(new StringValue("HEllo"))); + try { + Value res = mf.runFunction(ctr); + assertEquals(Value.VALUE_NUMBER, res.getValueType()); + assertEquals(5, ((NumberValue) res).getNumber().getAsInt()); + } catch (ExecutionException ex) { + fail(); + } + } } @Test @@ -98,12 +110,5 @@ public void testInvalidParams() { Value res = mf.runFunction(ctr); }); } - { - MinimaFunction mf = fn.getNewFunction(); - mf.addParameter(new ConstantExpression(new StringValue("Hello World"))); - assertThrows(ExecutionException.class, () -> { - Value res = mf.runFunction(ctr); - }); - } } }