Skip to content

Commit

Permalink
Add additional hash functions keccak256 and sha256
Browse files Browse the repository at this point in the history
  • Loading branch information
mikera committed Feb 2, 2024
1 parent 52da773 commit 6c5c3c4
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 13 deletions.
12 changes: 12 additions & 0 deletions convex-core/src/main/cvx/convex/core/metadata.cvx
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,18 @@
{:code "(hash (encoding :foo))"}]
:signature [{:params [value]
:return Blob}]}}

keccak256
{:doc {:description "Calculates the 32-byte Keccak-256 cryptographic hash of a `blob` or an `address` (which is a specialized type of `blob`). Returns a 32-byte `blob`."
:examples [{:code "(keccak256 0x1234)"}]
:signature [{:params [value]
:return Blob}]}}

sha256
{:doc {:description "Calculates the 32-byte SHA-256 cryptographic hash of a `blob` or an `address` (which is a specialized type of `blob`). Returns a 32-byte `blob`."
:examples [{:code "(sha256 0x1234)"}]
:signature [{:params [value]
:return Blob}]}}

hash-map
{:doc {:description "Constructs a map with the given keys and values. If a key is repeated, the last value will overwrite previous ones."
Expand Down
16 changes: 14 additions & 2 deletions convex-core/src/main/java/convex/core/crypto/Hashing.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,16 +79,28 @@ public static MessageDigest getSHA256Digest() {
}

/**
* Computes the SHA3-256 hash of byte data
* Computes the SHA-256 hash of byte data
*
* @param data Byte array to hash
* @return SHA3-256 Hash value
* @return Hash value
*/
public static Hash sha256(byte[] data) {
MessageDigest md = getSHA256Digest();
byte[] hash = md.digest(data);
return Hash.wrap(hash);
}

/**
* Computes the KECCAK-256 hash of byte data
*
* @param data Byte array to hash
* @return Hash value
*/
public static Hash keccak256(byte[] data) {
MessageDigest md = getKeccak256Digest();
byte[] hash = md.digest(data);
return Hash.wrap(hash);
}

/**
* Computes the SHA-256 hash of a string
Expand Down
40 changes: 35 additions & 5 deletions convex-core/src/main/java/convex/core/lang/Core.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import convex.core.Constants;
import convex.core.ErrorCodes;
import convex.core.State;
import convex.core.crypto.Hashing;
import convex.core.data.ABlob;
import convex.core.data.ACell;
import convex.core.data.ACountable;
Expand Down Expand Up @@ -2061,21 +2062,51 @@ public Context invoke(Context context, ACell[] args) {
});

public static final CoreFn<Hash> HASH = reg(new CoreFn<>(Symbols.HASH) {

@Override
public Context invoke(Context context, ACell[] args) {
if (args.length != 1) return context.withArityError(exactArityMessage(1, args.length));

ABlob blob=RT.ensureBlob(args[0]);
if (blob==null) return context.withCastError(0,args, Types.BLOB);
long juice=Juice.HASH+blob.count()*Juice.HASH_PER_BYTE;
if (!context.checkJuice(juice)) return context.withJuiceError();

Hash result = blob.getContentHash();
return context.withResult(Juice.HASH, result);
return context.withResult(juice, result);
}
});

public static final CoreFn<Hash> KECCAK256 = reg(new CoreFn<>(Symbols.KECCAK256) {
@Override
public Context invoke(Context context, ACell[] args) {
if (args.length != 1) return context.withArityError(exactArityMessage(1, args.length));

public static final CoreFn<CVMLong> COUNT = reg(new CoreFn<>(Symbols.COUNT) {

ABlob blob=RT.ensureBlob(args[0]);
if (blob==null) return context.withCastError(0,args, Types.BLOB);
long juice=Juice.HASH+blob.count()*Juice.HASH_PER_BYTE;
if (!context.checkJuice(juice)) return context.withJuiceError();

Hash result = blob.computeHash(Hashing.getKeccak256Digest());
return context.withResult(juice, result);
}
});

public static final CoreFn<Hash> SHA256 = reg(new CoreFn<>(Symbols.SHA256) {
@Override
public Context invoke(Context context, ACell[] args) {
if (args.length != 1) return context.withArityError(exactArityMessage(1, args.length));

ABlob blob=RT.ensureBlob(args[0]);
if (blob==null) return context.withCastError(0,args, Types.BLOB);
long juice=Juice.HASH+blob.count()*Juice.HASH_PER_BYTE;
if (!context.checkJuice(juice)) return context.withJuiceError();

Hash result = blob.computeHash(Hashing.getSHA256Digest());
return context.withResult(juice, result);
}
});

public static final CoreFn<CVMLong> COUNT = reg(new CoreFn<>(Symbols.COUNT) {
@Override
public Context invoke(Context context, ACell[] args) {
if (args.length != 1) return context.withArityError(exactArityMessage(1, args.length));
Expand All @@ -2088,7 +2119,6 @@ public Context invoke(Context context, ACell[] args) {
});

public static final CoreFn<ACell> EMPTY = reg(new CoreFn<>(Symbols.EMPTY) {

@Override
public Context invoke(Context context, ACell[] args) {
if (args.length != 1) return context.withArityError(exactArityMessage(1, args.length));
Expand Down
9 changes: 7 additions & 2 deletions convex-core/src/main/java/convex/core/lang/Juice.java
Original file line number Diff line number Diff line change
Expand Up @@ -152,11 +152,16 @@ public class Juice {
public static final long APPLY = 50;

/**
* Juice for a cryptographic hash
* Base juice for a cryptographic hash
*
* Expensive.
*/
public static final long HASH = 10000;
public static final long HASH = 1000;

/**
* Juice per byte of a cryptographic hash
*/
public static final long HASH_PER_BYTE = 10;

/**
* Juice for a very cheap operation. O(1), no new cell allocations or non-trivial lookups.
Expand Down
5 changes: 3 additions & 2 deletions convex-core/src/main/java/convex/core/lang/Symbols.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,10 @@ public class Symbols {
public static final Symbol CATCH = intern("catch");

public static final Symbol APPLY = intern("apply");

public static final Symbol HASH = intern("hash");
public static final Symbol KECCAK256 = intern("keccak256");
public static final Symbol SHA256 = intern("sha256");

public static final Symbol QUOTE = intern("quote");
public static final Symbol QUASIQUOTE = intern("quasiquote");
Expand Down Expand Up @@ -325,8 +328,6 @@ public class Symbols {
public static final Symbol MEMORY_VALUE = intern("memory-value");
public static final Symbol PROTOCOL = intern("protocol");



public static Symbol intern(String s) {
AString name=Strings.create(s);
Symbol sym=Symbol.create(name);
Expand Down
2 changes: 1 addition & 1 deletion convex-core/src/main/java/convex/core/util/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -722,7 +722,7 @@ public static int toInt(Object v) {
public static String readResourceAsString(String path) throws IOException {
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
try (InputStream inputStream = classLoader.getResourceAsStream(path)) {
if (inputStream == null) throw new IOException("Resource not found: " + path);
if (inputStream == null) throw new IOException("Resource not found: " + path + " with classloader "+classLoader);
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
return reader.lines().collect(Collectors.joining(System.lineSeparator()));
}
Expand Down
33 changes: 33 additions & 0 deletions convex-core/src/test/java/convex/core/lang/CoreTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3391,6 +3391,7 @@ public void testExp() {
@Test
public void testHash() {
assertEquals(Hash.fromHex("a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a"),eval("(hash 0x)"));
assertEquals(Hash.fromHex("352B82608DAD6C7AC3DD665BC2666E5D97803CB13F23A1109E2105E93F42C448"),eval("(hash 0xDEADBEEF)"));

assertEquals(Hash.NULL_HASH, eval("(hash (encoding nil))"));
assertEquals(Hash.TRUE_HASH, eval("(hash (encoding true))"));
Expand All @@ -3406,6 +3407,38 @@ public void testHash() {
assertArityError(step("(hash)"));
assertArityError(step("(hash nil nil)"));
}

@Test
public void testKeccak() {
assertEquals(Hash.fromHex("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"),eval("(keccak256 0x)"));

assertTrue(evalB("(= (keccak256 0x1234) (keccak256 0x1234))"));
assertTrue(evalB("(blob? (keccak256 0x00))")); // Should be a Blob

assertCastError(step("(keccak256 nil)"));
assertCastError(step("(keccak256 :foo)"));
assertCastError(step("(keccak256 #44)")); // specialised blobs don't implicitly cast to Blob

assertArityError(step("(keccak256)"));
assertArityError(step("(keccak256 nil nil)"));
}

@Test
public void testSHA256() {
assertEquals(Hash.fromHex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"),eval("(sha256 0x)"));
assertEquals(Hash.fromHex("5F78C33274E43FA9DE5659265C1D917E25C03722DCB0B8D27DB8D5FEAA813953"),eval("(sha256 0xdeadbeef)"));

assertTrue(evalB("(= (sha256 0x1234) (sha256 0x1234))"));
assertTrue(evalB("(blob? (sha256 0x00))")); // Should be a Blob

assertCastError(step("(sha256 nil)"));
assertCastError(step("(sha256 :foo)"));
assertCastError(step("(sha256 #44)")); // specialised blobs don't implicitly cast to Blob

assertArityError(step("(sha256)"));
assertArityError(step("(sha256 nil nil)"));
}


@Test
public void testCount() {
Expand Down
1 change: 0 additions & 1 deletion convex-core/src/test/java/convex/lib/SimpleNFTTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ private static State createState() {
assertNotError(c);
}


@SuppressWarnings("unchecked")
@Test public void testAssetAPI() {
Context ctx=context();
Expand Down

0 comments on commit 6c5c3c4

Please sign in to comment.