diff --git a/graphenej/src/main/java/cy/agorise/graphenej/HtlcHash.java b/graphenej/src/main/java/cy/agorise/graphenej/HtlcHash.java new file mode 100644 index 0000000..feffe22 --- /dev/null +++ b/graphenej/src/main/java/cy/agorise/graphenej/HtlcHash.java @@ -0,0 +1,24 @@ +package cy.agorise.graphenej; + +import com.google.common.primitives.Bytes; + +import cy.agorise.graphenej.interfaces.ByteSerializable; + +/** + * Class used to represent a HTLC hash. + */ +public class HtlcHash implements ByteSerializable { + private HtlcHashType hashType; + private byte[] hash; + + public HtlcHash(HtlcHashType hashType, byte[] hash) { + this.hashType = hashType; + this.hash = hash; + } + + @Override + public byte[] toBytes() { + byte[] hashTypeBytes = new byte[] { Util.revertInteger(hashType.ordinal())[3] }; + return Bytes.concat(hashTypeBytes, hash); + } +} diff --git a/graphenej/src/main/java/cy/agorise/graphenej/HtlcHashType.java b/graphenej/src/main/java/cy/agorise/graphenej/HtlcHashType.java new file mode 100644 index 0000000..ba66fec --- /dev/null +++ b/graphenej/src/main/java/cy/agorise/graphenej/HtlcHashType.java @@ -0,0 +1,11 @@ +package cy.agorise.graphenej; + +/** + * Used to enumerate the possible hash algorithms used in HTLCs. + * @see htlc.hpp + */ +public enum HtlcHashType { + RIPEMD160, + SHA1, + SHA256 +} diff --git a/graphenej/src/main/java/cy/agorise/graphenej/operations/CreateHtlcOperation.java b/graphenej/src/main/java/cy/agorise/graphenej/operations/CreateHtlcOperation.java new file mode 100644 index 0000000..0b41b2c --- /dev/null +++ b/graphenej/src/main/java/cy/agorise/graphenej/operations/CreateHtlcOperation.java @@ -0,0 +1,117 @@ +package cy.agorise.graphenej.operations; + +import com.google.common.primitives.Bytes; + +import cy.agorise.graphenej.AssetAmount; +import cy.agorise.graphenej.BaseOperation; +import cy.agorise.graphenej.HtlcHash; +import cy.agorise.graphenej.OperationType; +import cy.agorise.graphenej.UserAccount; +import cy.agorise.graphenej.Util; + +public class CreateHtlcOperation extends BaseOperation { + private AssetAmount fee; + private UserAccount from; + private UserAccount to; + private AssetAmount amount; + private HtlcHash preimageHash; + private short preimageSize; + private int claimPeriodSeconds; + + /** + * Public constructor + * + * @param fee The operation fee. + * @param from The source account. + * @param to The destination account. + * @param amount The amount to be traded. + * @param hash The pre-image hash. + * @param preimageSize The pre-image size. + * @param claimPeriodSeconds The claim period, in seconds. + */ + public CreateHtlcOperation(AssetAmount fee, UserAccount from, UserAccount to, AssetAmount amount, HtlcHash hash, short preimageSize, int claimPeriodSeconds) { + super(OperationType.HTLC_CREATE_OPERATION); + this.fee = fee; + this.from = from; + this.to = to; + this.amount = amount; + this.preimageHash = hash; + this.preimageSize = preimageSize; + this.claimPeriodSeconds = claimPeriodSeconds; + } + + @Override + public void setFee(AssetAmount newFee){ + this.fee = newFee; + } + + public AssetAmount getFee() { + return fee; + } + + public UserAccount getFrom() { + return from; + } + + public void setFrom(UserAccount from) { + this.from = from; + } + + public UserAccount getTo() { + return to; + } + + public void setTo(UserAccount to) { + this.to = to; + } + + public AssetAmount getAmount() { + return amount; + } + + public void setAmount(AssetAmount amount) { + this.amount = amount; + } + + public HtlcHash getPreimageHash() { + return preimageHash; + } + + public void setPreimageHash(HtlcHash preimageHash) { + this.preimageHash = preimageHash; + } + + public short getPreimageSize() { + return preimageSize; + } + + public void setPreimageSize(short preimageSize) { + this.preimageSize = preimageSize; + } + + public int getClaimPeriodSeconds() { + return claimPeriodSeconds; + } + + public void setClaimPeriodSeconds(int claimPeriodSeconds) { + this.claimPeriodSeconds = claimPeriodSeconds; + } + + @Override + public byte[] toBytes() { + byte[] feeBytes = fee.toBytes(); + byte[] fromBytes = from.toBytes(); + byte[] toBytes = to.toBytes(); + byte[] amountBytes = amount.toBytes(); + byte[] htlcHashBytes = preimageHash.toBytes(); + byte[] preimageSizeBytes = Util.revertShort(preimageSize); + byte[] claimPeriodBytes = Util.revertInteger(claimPeriodSeconds); + byte[] extensionsBytes = extensions.toBytes(); + return Bytes.concat(feeBytes, fromBytes, toBytes, amountBytes, htlcHashBytes, preimageSizeBytes, claimPeriodBytes, extensionsBytes); + } + + @Override + public String toJsonString() { + return null; + } +} diff --git a/graphenej/src/test/java/cy/agorise/graphenej/operations/CreateHtlcOperationTest.java b/graphenej/src/test/java/cy/agorise/graphenej/operations/CreateHtlcOperationTest.java new file mode 100644 index 0000000..888a498 --- /dev/null +++ b/graphenej/src/test/java/cy/agorise/graphenej/operations/CreateHtlcOperationTest.java @@ -0,0 +1,69 @@ +package cy.agorise.graphenej.operations; + +import com.google.common.primitives.UnsignedLong; + +import org.junit.Assert; +import org.junit.Test; + +import java.security.NoSuchAlgorithmException; + +import cy.agorise.graphenej.Asset; +import cy.agorise.graphenej.AssetAmount; +import cy.agorise.graphenej.HtlcHash; +import cy.agorise.graphenej.HtlcHashType; +import cy.agorise.graphenej.UserAccount; +import cy.agorise.graphenej.Util; + +public class CreateHtlcOperationTest { + private final String SERIALIZED_OP = "f68585abf4dce7c8045701310000000000000000007b7c80241100000000000000a06e327ea7388c18e4740e350ed4e60f2e04fc41c80078000000000001"; + private final String PREIMAGE_HEX = "666f6f626172"; + private final String HASH_RIPEMD160 = "a06e327ea7388c18e4740e350ed4e60f2e04fc41"; + private final String HASH_SHA1 = "8843d7f92416211de9ebb963ff4ce28125932878"; + private final String HASH_SHA256 = "c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2"; + + private final Asset CORE = new Asset("1.3.0"); + + @Test + public void testRipemd160(){ + try { + byte[] hashRipemd160 = Util.htlcHash(Util.hexToBytes(PREIMAGE_HEX), HtlcHashType.RIPEMD160); + String hexHash = Util.bytesToHex(hashRipemd160); + Assert.assertEquals(HASH_RIPEMD160, hexHash); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + } + + @Test + public void testSha1(){ + try { + byte[] hashSha1 = Util.htlcHash(Util.hexToBytes(PREIMAGE_HEX), HtlcHashType.SHA1); + String hexHash = Util.bytesToHex(hashSha1); + Assert.assertEquals(HASH_SHA1, hexHash); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + } + + @Test + public void testSha256(){ + try { + byte[] hashSha256 = Util.htlcHash(Util.hexToBytes(PREIMAGE_HEX), HtlcHashType.SHA256); + String hexHash = Util.bytesToHex(hashSha256); + Assert.assertEquals(HASH_SHA256, hexHash); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + } + + //TODO: Implement operation test + public void testOperationSerialization() throws NoSuchAlgorithmException { + UserAccount from = new UserAccount("1.2.123"); + UserAccount to = new UserAccount("1.2.124"); + AssetAmount fee = new AssetAmount(UnsignedLong.valueOf(0), CORE); + AssetAmount amount = new AssetAmount(UnsignedLong.valueOf(1123456), CORE); + byte[] hashBytes = Util.htlcHash("foobar".getBytes(), HtlcHashType.RIPEMD160); + HtlcHash preimageHash = new HtlcHash(HtlcHashType.RIPEMD160, hashBytes); + CreateHtlcOperation operation = new CreateHtlcOperation(fee, from, to, amount, preimageHash, (short) 200, 120); + } +}