package com.yoghurt.crypto.transactions.client.util; import com.googlecode.gwt.crypto.bouncycastle.util.encoders.Hex; import com.yoghurt.crypto.transactions.client.place.AddressPlace; import com.yoghurt.crypto.transactions.client.place.AddressPlace.AddressDataType; import com.yoghurt.crypto.transactions.client.place.ApplicationPlace; import com.yoghurt.crypto.transactions.client.place.BlockPlace; import com.yoghurt.crypto.transactions.client.place.BlockPlace.BlockDataType; import com.yoghurt.crypto.transactions.client.place.ConfigPlace; import com.yoghurt.crypto.transactions.client.place.ContributePlace; import com.yoghurt.crypto.transactions.client.place.MinePlace; import com.yoghurt.crypto.transactions.client.place.MinePlace.MineDataType; import com.yoghurt.crypto.transactions.client.place.RPCResponsePlace; import com.yoghurt.crypto.transactions.client.place.TransactionPlace; import com.yoghurt.crypto.transactions.client.place.TransactionPlace.TransactionDataType; import com.yoghurt.crypto.transactions.client.util.address.AddressParseUtil; import com.yoghurt.crypto.transactions.client.util.block.BlockParseUtil; import com.yoghurt.crypto.transactions.client.util.transaction.TransactionParseUtil; import com.yoghurt.crypto.transactions.shared.domain.Base58CheckContents; import com.yoghurt.crypto.transactions.shared.domain.Block; import com.yoghurt.crypto.transactions.shared.domain.JSONRPCMethod; import com.yoghurt.crypto.transactions.shared.domain.Transaction; public final class PlaceTokenParseUtil { private static final String MINE_TOKEN = "mine"; private static final String LAST_BLOCK_TOKEN = "last"; private static final String CONFIG_TOKEN = "config"; private static final String CONTRIBUTE_TOKEN = "contribute"; private static final String NBSP = " "; // Bunch of zeroes every valid block starts with private static final String BLOCK_TOKEN_START = "0000000000"; // Hash length (256 bit) if encoded into hex private static final int HASH_LENGTH = 64; // Allows for block heights up to 9999999, will last until the year 2190 or // so, I guess we're good private static final int MAX_BLOCK_HEIGHT_NUMBER_LENGTH = 7; private PlaceTokenParseUtil() {} public static ApplicationPlace parseToken(final String token) { // Clean up the token first final String cleanToken = token.replace(" ", ""); // Check for keywords if (MINE_TOKEN.equals(cleanToken)) { return new MinePlace(MineDataType.LAST); } if (LAST_BLOCK_TOKEN.equals(cleanToken)) { return new BlockPlace(BlockDataType.LAST); } if (CONFIG_TOKEN.equals(cleanToken)) { return new ConfigPlace(); } if (CONTRIBUTE_TOKEN.equals(cleanToken)) { return new ContributePlace(); } final String[] splitToken = token.split(NBSP); if (JSONRPCMethod.fromName(splitToken[0]) != null) { return new RPCResponsePlace(splitToken); } // Check if the token can be base58 decoded, and if so, if the result looks // like an address (20 byte payload) try { final Base58CheckContents address = AddressParseUtil.parseAddress(cleanToken); if (address.getPayload().length == 20) { return new AddressPlace(AddressDataType.ID, cleanToken); } } catch (final Exception e) { // Guess not. } // Check if the token is exactly equal to the length of a hash (32 bytes), meaning this // is probably a transaction or block hash if (cleanToken.length() == HASH_LENGTH) { // If it starts with a bunch of zeroes, it's probably a block, otherwise // it may be a transaction if (cleanToken.startsWith(BLOCK_TOKEN_START)) { return new BlockPlace(BlockDataType.ID, cleanToken); } else { return new TransactionPlace(TransactionDataType.ID, cleanToken); } } // If the token is longer than the hash size, this could be a raw // transaction or a raw block, so try and parse it if (cleanToken.length() > HASH_LENGTH) { try { // If this works out, it's a transaction! final Transaction t = new Transaction(); TransactionParseUtil.parseTransactionBytes(Hex.decode(cleanToken), t); return new TransactionPlace(TransactionDataType.RAW, cleanToken); } catch (final Exception e1) { try { // If this works out, it's a block! final Block b = new Block(); BlockParseUtil.parseBlockBytes(Hex.decode(cleanToken), b); return new BlockPlace(BlockDataType.RAW, cleanToken); } catch (final Exception e2) { // Eat, this is neither a raw transaction or block } } } // Check if the token is a number, probably block height if it is if (cleanToken.length() <= MAX_BLOCK_HEIGHT_NUMBER_LENGTH) { try { final int possibleBlockHeight = Integer.parseInt(cleanToken); // Can't be less than 0 though.. if (possibleBlockHeight >= 0) { return new BlockPlace(BlockDataType.HEIGHT, possibleBlockHeight); } } catch (final NumberFormatException e) { // Eat, this is probably not a number ;) } } // Nope, don't know what to do with this, return null return null; } }