From da47075c6e9c9acbbed7abefaf579de4428e8b78 Mon Sep 17 00:00:00 2001 From: mikera Date: Fri, 15 Dec 2023 12:51:14 +0000 Subject: [PATCH] Improve behaviour of integer <-> blob conversions. Fixes #432 --- .../main/java/convex/cli/AccountCreate.java | 2 +- .../java/convex/cli/AccountInformation.java | 2 +- .../src/main/java/convex/cli/PeerCreate.java | 2 +- .../src/main/java/convex/core/State.java | 6 ++-- .../java/convex/core/data/AArrayBlob.java | 31 +++-------------- .../src/main/java/convex/core/data/ABlob.java | 8 ----- .../main/java/convex/core/data/ALongBlob.java | 9 ++--- .../main/java/convex/core/data/Address.java | 11 ++----- .../main/java/convex/core/data/BlobTree.java | 7 ---- .../main/java/convex/core/data/LongBlob.java | 13 +++----- .../java/convex/core/data/prim/CVMDouble.java | 2 +- .../java/convex/core/data/prim/CVMLong.java | 11 +++++-- .../src/main/java/convex/core/init/Init.java | 14 ++++---- .../main/java/convex/core/lang/Context.java | 10 +++--- .../src/main/java/convex/core/lang/RT.java | 2 +- .../src/main/java/convex/core/util/Utils.java | 4 +-- .../convex/core/cpos/BeliefMergeTest.java | 2 +- .../java/convex/core/data/AddressTest.java | 2 +- .../test/java/convex/core/data/BlobsTest.java | 10 +++--- .../java/convex/core/data/prim/LongTest.java | 10 ++++-- .../test/java/convex/core/lang/CoreTest.java | 33 ++++++++++--------- .../java/convex/core/lang/NumericsTest.java | 4 +-- .../src/test/java/convex/util/UtilsTest.java | 10 +++--- .../src/main/java/convex/java/Convex.java | 12 +++---- .../java/convex/restapi/api/ChainAPI.java | 2 +- .../java/convex/restapi/RemoteClientTest.java | 4 +-- 26 files changed, 93 insertions(+), 130 deletions(-) diff --git a/convex-cli/src/main/java/convex/cli/AccountCreate.java b/convex-cli/src/main/java/convex/cli/AccountCreate.java index 40f4a802c..4c3eb2fd2 100644 --- a/convex-cli/src/main/java/convex/cli/AccountCreate.java +++ b/convex-cli/src/main/java/convex/cli/AccountCreate.java @@ -76,7 +76,7 @@ public void run() { convex = mainParent.connectAsPeer(0); Address address = convex.createAccountSync(keyPair.getAccountKey()); - output.addField("Address", address.toExactLong()); + output.addField("Address", address.longValue()); if (isFund) { convex.transferSync(address, Constants.ACCOUNT_FUND_AMOUNT); convex = mainParent.connectToSessionPeer(hostname, port, address, keyPair); diff --git a/convex-cli/src/main/java/convex/cli/AccountInformation.java b/convex-cli/src/main/java/convex/cli/AccountInformation.java index bf62a9a4b..6762edf78 100644 --- a/convex-cli/src/main/java/convex/cli/AccountInformation.java +++ b/convex-cli/src/main/java/convex/cli/AccountInformation.java @@ -64,7 +64,7 @@ public void run() { Convex convex = null; Address address = Address.create(addressNumber); convex = mainParent.connectToSessionPeer(hostname, port, address, null); - String queryCommand = String.format("(account #%d)", address.toExactLong()); + String queryCommand = String.format("(account #%d)", address.longValue()); ACell message = Reader.read(queryCommand); Result result; try { diff --git a/convex-cli/src/main/java/convex/cli/PeerCreate.java b/convex-cli/src/main/java/convex/cli/PeerCreate.java index e8ee546b0..2b16b0086 100644 --- a/convex-cli/src/main/java/convex/cli/PeerCreate.java +++ b/convex-cli/src/main/java/convex/cli/PeerCreate.java @@ -121,7 +121,7 @@ public void run() { RecordOutput output=new RecordOutput(); output.addField("Public Peer Key", keyPair.getAccountKey().toString()); - output.addField("Address", address.toExactLong()); + output.addField("Address", address.longValue()); output.addField("Balance", currentBalance); output.addField("Inital stake amount", stakeAmount); // System.out.println("You can now start this peer by executing the following line:\n"); diff --git a/convex-core/src/main/java/convex/core/State.java b/convex-core/src/main/java/convex/core/State.java index 390e68a4e..5e3139b63 100644 --- a/convex-core/src/main/java/convex/core/State.java +++ b/convex-core/src/main/java/convex/core/State.java @@ -382,7 +382,7 @@ public State applyScheduledTransactions() { if (sched.isEmpty()) break; MapEntry> me = sched.entryAt(0); ABlob key = me.getKey(); - long time = key.toExactLong(); + long time = key.longValue(); if (time > timestamp.longValue()) break; // exit if we are still in the future AVector trans = me.getValue(); long numScheduled = trans.count(); // number scheduled at this schedule timestamp @@ -610,7 +610,7 @@ public State withAccounts(AVector newAccounts) { * @throws IndexOutOfBoundsException if Address represents an illegal account position */ public State putAccount(Address address, AccountStatus accountStatus) { - long ix=address.toExactLong(); + long ix=address.longValue(); long n=accounts.count(); if (ix>n) { throw new IndexOutOfBoundsException("Trying to add an account beyond accounts array at position: "+ix); @@ -634,7 +634,7 @@ public State putAccount(Address address, AccountStatus accountStatus) { * @return The AccountStatus for the given account, or null. */ public AccountStatus getAccount(Address target) { - long ix=target.toExactLong(); + long ix=target.longValue(); if ((ix<0)||(ix>=accounts.count())) return null; return accounts.get(ix); } diff --git a/convex-core/src/main/java/convex/core/data/AArrayBlob.java b/convex-core/src/main/java/convex/core/data/AArrayBlob.java index 994cac223..9ae654b7e 100644 --- a/convex-core/src/main/java/convex/core/data/AArrayBlob.java +++ b/convex-core/src/main/java/convex/core/data/AArrayBlob.java @@ -6,7 +6,6 @@ import convex.core.crypto.Hashing; import convex.core.exceptions.InvalidDataException; -import convex.core.util.Errors; import convex.core.util.Utils; /** @@ -178,7 +177,7 @@ public long longAt(long i) { throw new IndexOutOfBoundsException("Index: " + i); } - long val=Utils.readLong(store, offset+ix); + long val=Utils.readLong(store, offset+ix,8); return val; } @@ -328,35 +327,13 @@ public void validateCell() throws InvalidDataException { } } - @Override - public long toExactLong() { - if (length != 8) throw new IllegalStateException(Errors.wrongLength(8, length)); - return Utils.readLong(store, offset); - } - @Override public long longValue() { + if (length==0) return 0; if (length >= 8) { - return Utils.readLong(store, offset + length - 8); + return Utils.readLong(store, offset + length - 8,8); } else { - long result = 0l; - int ix = offset; - if ((length & 4) != 0) { - result += 0xffffffffL & Utils.readInt(store, ix); - ix += 4; - } - if ((length & 2) != 0) { - result = (result << 16) + (0xFFFF & Utils.readShort(store, ix)); - ix += 2; - } - if ((length & 1) != 0) { - result = (result << 8) + (0xFF & store[ix]); - ix += 1; - } - // Sign extend - //int shift=64-(length*8); - //return (result<>shift; - return result; + return Utils.readLong(store,offset,length); } } diff --git a/convex-core/src/main/java/convex/core/data/ABlob.java b/convex-core/src/main/java/convex/core/data/ABlob.java index d4b72c51b..4206a39b8 100644 --- a/convex-core/src/main/java/convex/core/data/ABlob.java +++ b/convex-core/src/main/java/convex/core/data/ABlob.java @@ -343,14 +343,6 @@ public final int hashCode() { return Long.hashCode(longValue()); } - /** - * Gets the long value of this Blob if the length is exactly 8 bytes, otherwise - * throws an Exception - * - * @return The long value represented by the Blob - */ - public abstract long toExactLong(); - /** * Returns true if this object is a regular blob (i.e. not a special blob type like Address) * @return True if a regular blob diff --git a/convex-core/src/main/java/convex/core/data/ALongBlob.java b/convex-core/src/main/java/convex/core/data/ALongBlob.java index 98ed2c8cc..719f4b363 100644 --- a/convex-core/src/main/java/convex/core/data/ALongBlob.java +++ b/convex-core/src/main/java/convex/core/data/ALongBlob.java @@ -111,16 +111,11 @@ public long hexMatchLength(ABlob b, long start, long length) { public final long longValue() { return value; } - - @Override - public long toExactLong() { - return value; - } @Override public int compareTo(ABlob b) { if (b.count()==LENGTH) { - return compareTo(b.toExactLong()); + return compareTo(b.longValue()); } else { return -b.compareTo(this); } @@ -133,7 +128,7 @@ protected int compareTo(long bvalue) { @Override public final boolean equalsBytes(ABlob b) { if (b.count()!=LENGTH) return false; - return value==b.toExactLong(); + return value==b.longValue(); } @Override diff --git a/convex-core/src/main/java/convex/core/data/Address.java b/convex-core/src/main/java/convex/core/data/Address.java index a00c122e0..dc4d026e7 100644 --- a/convex-core/src/main/java/convex/core/data/Address.java +++ b/convex-core/src/main/java/convex/core/data/Address.java @@ -51,7 +51,7 @@ public static Address create(long number) { */ public static Address create(ABlob b) { if (b.count()!=BYTE_LENGTH) return null; - return create(b.toExactLong()); + return create(b.longValue()); } @Override @@ -94,7 +94,7 @@ public static Address fromHex(String hexString) { Blob b=Blob.fromHex(hexString); if (b==null) return null; if (b.length!=BYTE_LENGTH) return null; - return create(b.toExactLong()); + return create(b.longValue()); } /** @@ -219,12 +219,7 @@ protected void updateDigest(MessageDigest digest) { @Override public boolean equalsBytes(byte[] bytes, int byteOffset) { - return value==Utils.readLong(bytes, byteOffset); - } - - @Override - public long toExactLong() { - return value; + return value==Utils.readLong(bytes, byteOffset,8); } public static final int MAX_ENCODING_LENGTH = 1+Format.MAX_VLC_COUNT_LENGTH; diff --git a/convex-core/src/main/java/convex/core/data/BlobTree.java b/convex-core/src/main/java/convex/core/data/BlobTree.java index dd2f64f83..fa59bced3 100644 --- a/convex-core/src/main/java/convex/core/data/BlobTree.java +++ b/convex-core/src/main/java/convex/core/data/BlobTree.java @@ -5,7 +5,6 @@ import convex.core.exceptions.BadFormatException; import convex.core.exceptions.InvalidDataException; -import convex.core.util.Errors; import convex.core.util.Utils; /** @@ -524,12 +523,6 @@ public long hexMatchLength(ABlob b, long start, long length) { } return length; } - - @Override - public long toExactLong() { - if (count != 8) throw new IllegalStateException(Errors.wrongLength(8, count)); - return getChunk(0).toExactLong(); - } @Override public long longValue() { diff --git a/convex-core/src/main/java/convex/core/data/LongBlob.java b/convex-core/src/main/java/convex/core/data/LongBlob.java index 669aa1e41..2575caf7c 100644 --- a/convex-core/src/main/java/convex/core/data/LongBlob.java +++ b/convex-core/src/main/java/convex/core/data/LongBlob.java @@ -22,8 +22,8 @@ private LongBlob(long value) { public static LongBlob create(String string) { byte[] bs = Utils.hexToBytes(string); - if (bs.length != LENGTH) throw new IllegalArgumentException("Long blob requires a length 8 hex string"); - return new LongBlob(Utils.readLong(bs, 0)); + int bl=bs.length; + return new LongBlob(Utils.readLong(bs, Math.max(0, bl-8),Math.min(8, bl))); } public static LongBlob create(long value) { @@ -75,7 +75,7 @@ public boolean equals(ABlob a) { // Note Blob is the only other plausible representation of a LongBlob // AccountKey, Hash etc. excluded because of length Blob b=(Blob)a; - return ((b.count()==LENGTH)&& (b.toExactLong()== value)); + return ((b.count()==LENGTH)&& (b.longValue()== value)); } return false; } @@ -98,11 +98,6 @@ public int estimatedEncodingSize() { return (int) (2 + LENGTH); } - @Override - public long toExactLong() { - return value; - } - @Override public long hexMatchLength(ABlob b, long start, long length) { if (b == this) return length; @@ -120,7 +115,7 @@ public byte getTag() { @Override public boolean equalsBytes(byte[] bytes, int byteOffset) { - return value==Utils.readLong(bytes, byteOffset); + return value==Utils.readLong(bytes, byteOffset,8); } @Override diff --git a/convex-core/src/main/java/convex/core/data/prim/CVMDouble.java b/convex-core/src/main/java/convex/core/data/prim/CVMDouble.java index 767f3d92a..c111bf587 100644 --- a/convex-core/src/main/java/convex/core/data/prim/CVMDouble.java +++ b/convex-core/src/main/java/convex/core/data/prim/CVMDouble.java @@ -165,7 +165,7 @@ public static CVMDouble read(double value) throws BadFormatException { public static CVMDouble read(byte tag, Blob blob, int offset) throws BadFormatException { if (blob.count() genesisKeys) { accts = s.getAccounts(); // Set up initial user accounts - assert(accts.count() == GENESIS_ADDRESS.toExactLong()); + assert(accts.count() == GENESIS_ADDRESS.longValue()); { long userFunds = (long)(supply*0.8); // 80% to user accounts supply -= userFunds; @@ -128,7 +128,7 @@ public static State createBaseState(List genesisKeys) { // One Peer account for each specified key (including initial genesis user) for (int i = 0; i < keyCount; i++) { Address address = Address.create(accts.count()); - assert(address.toExactLong() == accts.count()); + assert(address.longValue() == accts.count()); AccountKey key = genesisKeys.get(i); long userBalance = userFunds / (keyCount-i); accts = addAccount(accts, address, key, userBalance); @@ -250,11 +250,11 @@ private static State addStandardLibraries(State s) { } public static Address calcPeerAddress(int userCount, int index) { - return Address.create(GENESIS_ADDRESS.toExactLong() + userCount + index); + return Address.create(GENESIS_ADDRESS.longValue() + userCount + index); } public static Address calcUserAddress(int index) { - return Address.create(GENESIS_ADDRESS.toExactLong() + index); + return Address.create(GENESIS_ADDRESS.longValue() + index); } // A CVX file contains forms which must be wrapped in a `(do ...)` and deployed as an actor. @@ -337,14 +337,14 @@ private static BlobMap addPeer(BlobMap addGovernanceAccount(AVector accts, Address a, long balance) { - if (accts.count() != a.toExactLong()) throw new Error("Incorrect initialisation address: " + a); + if (accts.count() != a.longValue()) throw new Error("Incorrect initialisation address: " + a); AccountStatus as = AccountStatus.createGovernance(balance); accts = accts.conj(as); return accts; } private static AVector addCoreLibrary(AVector accts, Address a) { - if (accts.count() != a.toExactLong()) throw new Error("Incorrect core library address: " + a); + if (accts.count() != a.longValue()) throw new Error("Incorrect core library address: " + a); AccountStatus as = AccountStatus.createActor(); as=as.withEnvironment(Core.ENVIRONMENT); @@ -355,7 +355,7 @@ private static AVector addCoreLibrary(AVector acct private static AVector addAccount(AVector accts, Address a, AccountKey key, long balance) { - if (accts.count() != a.toExactLong()) throw new Error("Incorrect account address: " + a); + if (accts.count() != a.longValue()) throw new Error("Incorrect account address: " + a); AccountStatus as = AccountStatus.create(0L, balance, key); as = as.withMemory(Constants.INITIAL_ACCOUNT_ALLOWANCE); accts = accts.conj(as); diff --git a/convex-core/src/main/java/convex/core/lang/Context.java b/convex-core/src/main/java/convex/core/lang/Context.java index 3b43692b7..bfb60c984 100644 --- a/convex-core/src/main/java/convex/core/lang/Context.java +++ b/convex-core/src/main/java/convex/core/lang/Context.java @@ -1478,7 +1478,7 @@ public Context transfer(Address target, long amount) { AVector accounts=getState().getAccounts(); Address source=getAddress(); - long sourceIndex=source.toExactLong(); + long sourceIndex=source.longValue(); AccountStatus sourceAccount=accounts.get(sourceIndex); long currentBalance=sourceAccount.getBalance(); @@ -1491,7 +1491,7 @@ public Context transfer(Address target, long amount) { accounts=accounts.assoc(sourceIndex, newSourceAccount); // new target account (note: could be source account, so we get from latest accounts) - long targetIndex=target.toExactLong(); + long targetIndex=target.longValue(); if (targetIndex>=accounts.count()) { return this.withError(ErrorCodes.NOBODY,"Target account for transfer "+target+" does not exist"); } @@ -1538,7 +1538,7 @@ public Context transferMemoryAllowance(Address target, CVMLong amountToSend) { AVector accounts=getState().getAccounts(); Address source=getAddress(); - long sourceIndex=source.toExactLong(); + long sourceIndex=source.longValue(); AccountStatus sourceAccount=accounts.get(sourceIndex); long currentBalance=sourceAccount.getMemory(); @@ -1551,7 +1551,7 @@ public Context transferMemoryAllowance(Address target, CVMLong amountToSend) { accounts=accounts.assoc(sourceIndex, newSourceAccount); // new target account (note: could be source account, so we get from latest accounts) - long targetIndex=target.toExactLong(); + long targetIndex=target.longValue(); if (targetIndex>=accounts.count()) { return withError(ErrorCodes.NOBODY,"Cannot transfer memory allowance to non-existent account: "+target); } @@ -1579,7 +1579,7 @@ public Context setMemory(long allowance) { if (allowance>Constants.MAX_SUPPLY) return withError(ErrorCodes.ARGUMENT,"Can't transfer an allowance amount beyond maximum limit"); Address source=getAddress(); - long sourceIndex=source.toExactLong(); + long sourceIndex=source.longValue(); AccountStatus sourceAccount=accounts.get(sourceIndex); long current=sourceAccount.getMemory(); diff --git a/convex-core/src/main/java/convex/core/lang/RT.java b/convex-core/src/main/java/convex/core/lang/RT.java index 5da49496a..8dbe62d1c 100644 --- a/convex-core/src/main/java/convex/core/lang/RT.java +++ b/convex-core/src/main/java/convex/core/lang/RT.java @@ -1680,7 +1680,7 @@ public static T json(ACell o) { if (o instanceof CVMChar) return (T) ((CVMChar) o).toString(); if (o instanceof Address) - return (T) (Long)((Address) o).toExactLong(); + return (T) (Long)((Address) o).longValue(); if (o instanceof AMap) { AMap m= (AMap)o; return (T)jsonMap(m); diff --git a/convex-core/src/main/java/convex/core/util/Utils.java b/convex-core/src/main/java/convex/core/util/Utils.java index 97bf9e7e3..5ae41e52c 100644 --- a/convex-core/src/main/java/convex/core/util/Utils.java +++ b/convex-core/src/main/java/convex/core/util/Utils.java @@ -175,9 +175,9 @@ public static int readIntZeroExtend(byte[] data, int offset) { return result; } - public static long readLong(byte[] data, int offset) { + public static long readLong(byte[] data, int offset, int numBytes) { long result = data[offset]; - for (int i = 1; i <= 7; i++) { + for (int i = 1; i < numBytes; i++) { result = (result << 8) + (data[offset + i] & 0xFF); } return result; diff --git a/convex-core/src/test/java/convex/core/cpos/BeliefMergeTest.java b/convex-core/src/test/java/convex/core/cpos/BeliefMergeTest.java index 54e7ca527..0936ba6b6 100644 --- a/convex-core/src/test/java/convex/core/cpos/BeliefMergeTest.java +++ b/convex-core/src/test/java/convex/core/cpos/BeliefMergeTest.java @@ -467,7 +467,7 @@ public void testGossipConsensus() throws Exception { // should have correct number of transactions each for (int i = 0; i < NUM_PEERS; i++) { - assertEquals(NUM_INITIAL_TRANS+TX_ROUNDS, accounts.get(ADDRESSES[i].toExactLong()).getSequence()); + assertEquals(NUM_INITIAL_TRANS+TX_ROUNDS, accounts.get(ADDRESSES[i].longValue()).getSequence()); assertEquals(finalBlocks,bs4[i].getPeerOrder().getBlocks()); } // should have equal balance diff --git a/convex-core/src/test/java/convex/core/data/AddressTest.java b/convex-core/src/test/java/convex/core/data/AddressTest.java index 6f5e2aa83..13e52eee3 100644 --- a/convex-core/src/test/java/convex/core/data/AddressTest.java +++ b/convex-core/src/test/java/convex/core/data/AddressTest.java @@ -42,7 +42,7 @@ public void testCanonical() { @Test public void testBlobBehaviour() { - assertEquals(0L,Address.ZERO.toExactLong()); + assertEquals(0L,Address.ZERO.longValue()); assertEquals(Blobs.createFilled(0, 8),Address.ZERO.toFlatBlob()); } diff --git a/convex-core/src/test/java/convex/core/data/BlobsTest.java b/convex-core/src/test/java/convex/core/data/BlobsTest.java index dc8ac523f..15059b9eb 100644 --- a/convex-core/src/test/java/convex/core/data/BlobsTest.java +++ b/convex-core/src/test/java/convex/core/data/BlobsTest.java @@ -206,8 +206,8 @@ public void testLongBlob() { Blob bb = Blob.fromHex("cafebabedeadbeef"); assertEquals(b.getContentHash(),bb.getContentHash()); // same data hash - assertEquals(b.toExactLong(),bb.toExactLong()); - assertEquals(0xcafebabedeadbeefl,b.toExactLong()); + assertEquals(b.longValue(),bb.longValue()); + assertEquals(0xcafebabedeadbeefl,b.longValue()); assertEquals(10, b.getHexDigit(1)); // 'a' @@ -230,7 +230,7 @@ public void testLongBlob() { ObjectsTest.doEqualityTests(b, bb); - doLongBlobTests(b.toExactLong()); + doLongBlobTests(b.longValue()); } @Test @@ -254,8 +254,10 @@ private void doLongBlobTests(long v) { assertEquals(fb.hashCode(),b.hashCode()); // Test Address conversion - if (b.toExactLong()>=0) { + if (b.longValue()>=0) { assertEquals(b.toHexString(),Address.create(b).toHexString()); + } else { + assertNull(Address.create(b)); } doBlobTests(b); diff --git a/convex-core/src/test/java/convex/core/data/prim/LongTest.java b/convex-core/src/test/java/convex/core/data/prim/LongTest.java index d7d95d2f7..903a30e43 100644 --- a/convex-core/src/test/java/convex/core/data/prim/LongTest.java +++ b/convex-core/src/test/java/convex/core/data/prim/LongTest.java @@ -96,6 +96,11 @@ private String es(long v) { @Test public void testFromBlob() { assertEquals(0xff,Blob.fromHex("0000ff").longValue()); } + + @Test public void testToBlob() { + assertEquals(Blob.fromHex("00"),CVMLong.ZERO.toBlob()); + assertEquals(Blob.fromHex("ff"),CVMLong.MINUS_ONE.toBlob()); + } @Test public void testLongSamples() { doLongTest(CVMLong.ZERO); @@ -122,13 +127,14 @@ private String es(long v) { public void doLongTest(CVMLong a) { long val=a.longValue(); + long n=a.byteLength(); assertEquals(CVMLong.create(val),a); assertTrue(a.isCanonical()); assertTrue(a.isEmbedded()); - assertTrue(a.byteLength()<=8); + assertTrue(n<=8); assertEquals(val,a.longValue()); - assertEquals(val,a.toBlob().toExactLong()); + assertEquals(val,a.toBlob().longValue()); if (val!=0) { assertEquals(BigInteger.valueOf(val).toByteArray().length,a.byteLength()); diff --git a/convex-core/src/test/java/convex/core/lang/CoreTest.java b/convex-core/src/test/java/convex/core/lang/CoreTest.java index 3843d7382..ecea55f1f 100644 --- a/convex-core/src/test/java/convex/core/lang/CoreTest.java +++ b/convex-core/src/test/java/convex/core/lang/CoreTest.java @@ -103,7 +103,7 @@ public void testAddress() { assertEquals(a, eval("(address 0x" + a.toHexString() + ")")); assertEquals(a, eval("(address (address \"" + a.toHexString() + "\"))")); assertEquals(a, eval("(address (blob \"" + a.toHexString() + "\"))")); - assertEquals(a, eval("(address "+a.toExactLong()+")")); + assertEquals(a, eval("(address "+a.longValue()+")")); // bad arities assertArityError(step("(address 1 2)")); @@ -126,13 +126,14 @@ public void testBlob() { assertEquals("cafebabe", evalS("(str (blob \"Cafebabe\"))")); - assertSame(eval("0x"),eval("(blob (str))")); // blob literal + assertSame(eval("0x"),eval("(blob (str))")); // empty blob literal for empty string assertEquals(eval("*address*"),eval("(address (blob *address*))")); - // Blob from a long - assertEquals(eval("0x0000000000001234"),eval("(blob (long \\u1234))")); // blob literal - assertEquals(eval("0xffffffffffffffff"),eval("(blob -1)")); + // Blob from a long, note minimal representation + assertEquals(eval("0x1234"),eval("(blob (long \\u1234))")); // blob literal + assertEquals(eval("0xff"),eval("(blob -1)")); + assertEquals(eval("0x8000000000000000"),eval("(blob "+Long.MIN_VALUE+")")); // Blob from a bigint assertEquals(eval("0x00ffffffffffffffff"),eval("(blob 18446744073709551615)")); @@ -145,7 +146,7 @@ public void testBlob() { assertEquals(eval("*key*"),eval("(blob *key*)")); // Long converts to blob and back - assertTrue(evalB("(= 0xffffffffffffffff (blob -1))")); + assertTrue(evalB("(= 0xff (blob -1))")); assertTrue(evalB("(= -1 (long (blob -1)))")); // round trip back to Blob @@ -198,7 +199,7 @@ public void testBitAnd() { @Test public void testBitOr() { assertCVMEquals(0xfff0fff0fff0fff0l, eval("(bit-or (long 0xff00ff00ff00ff00) (long 0x0ff00ff00ff00ff0))")); - assertCVMEquals(0xff01, eval("(bit-or (long 0xff00) (byte 1))")); + assertCVMEquals(-255, eval("(bit-or (long 0xff00) (byte 1))")); assertCastError(step("(bit-or nil 1)")); assertCastError(step("(bit-or 20 :foo)")); @@ -348,9 +349,9 @@ public void testLong() { // Blob casts treat blob as extended long bits (zero extended if needed) assertEquals(4096L, evalL("(long 0x1000)")); - assertEquals(255L, evalL("(long 0xff)")); - assertEquals(4294967295L, evalL("(long 0xffffffff)")); - assertEquals(0xff00000050l, evalL("(long 0xff00000050)")); + assertEquals(-1L, evalL("(long 0xff)")); + assertEquals(-1L, evalL("(long 0xffffffff)")); + assertEquals(-4294967216L, evalL("(long 0xff00000050)")); assertEquals(-1L, evalL("(long 0xffffffffffffffff)")); assertEquals(255L, evalL("(long 0xff00000000000000ff)")); // only taking last 8 bytes assertEquals(-1L, evalL("(long 0xcafebabeffffffffffffffff)")); // interpret as big endian big integer @@ -398,11 +399,11 @@ public void testInt() { assertEquals(97L, evalL("(int \\a)")); assertEquals(2147483648L, evalL("(int 2147483648)")); - // Blob casts treat blob as extended long bits (zero extended if needed) + // Blob casts treat blob as extended long bits (sign extended if needed) assertEquals(4096L, evalL("(int 0x1000)")); - assertEquals(255L, evalL("(int 0xff)")); - assertEquals(4294967295L, evalL("(int 0xffffffff)")); - assertEquals(0xff00000050l, evalL("(int 0xff00000050)")); + assertEquals(-1L, evalL("(int 0xff)")); + assertEquals(-1L, evalL("(int 0xffffffff)")); + assertEquals(0x0cff00000050l, evalL("(int 0x0cff00000050)")); // Currently we allow bools to explicitly cast to longs like this. TODO: maybe reconsider? assertEquals(1L, evalL("(int true)")); @@ -2601,7 +2602,7 @@ public void testBalance() { public void testCreateAccount() { Context ctx=step("(create-account 0x817934590c058ee5b7f1265053eeb4cf77b869e14c33e7f85b2babc85d672bbc)"); Address addr=ctx.getResult(); - assertEquals(addr.toExactLong()+1,ctx.getState().getAccounts().count()); // should be last Address added + assertEquals(addr.longValue()+1,ctx.getState().getAccounts().count()); // should be last Address added // Query rollback should result in same account being created assertTrue(evalB("(= (query (create-account *key*)) (query (create-account *key*)))")); @@ -4223,7 +4224,7 @@ public void testScheduleExecution() throws BadSignatureException { State s = ctx.getState(); BlobMap> sched = s.getSchedule(); assertEquals(1L, sched.count()); - assertEquals(expectedTS, sched.entryAt(0).getKey().toExactLong()); + assertEquals(expectedTS, sched.entryAt(0).getKey().longValue()); assertTrue(step(ctx, "(do a)").isExceptional()); diff --git a/convex-core/src/test/java/convex/core/lang/NumericsTest.java b/convex-core/src/test/java/convex/core/lang/NumericsTest.java index 1a95894ad..2473cd1e7 100644 --- a/convex-core/src/test/java/convex/core/lang/NumericsTest.java +++ b/convex-core/src/test/java/convex/core/lang/NumericsTest.java @@ -414,7 +414,7 @@ public void testHexCasts() { assertSame(CVMLong.forByte((byte)-1), eval("(byte 0xFF)")); // check we are treating blobs as unsigned values - assertEquals(510L, evalL("(+ (long 0xFF) (long 0xFF))")); + assertEquals(-2L, evalL("(+ (long 0xFF) (long 0xFF))")); assertEquals(-2L, evalL("(+ (long 0xFFFFFFFFFFFFFFFF) (long 0xFFFFFFFFFFFFFFFF))")); // take low order bytes of big long @@ -432,7 +432,7 @@ public void testBadArgs() { public void testCasts() { assertEquals(0L, evalL("(long (byte 256))")); assertEquals(13L, evalL("(long #13)")); - assertEquals(255L, evalL("(long 0xff)")); + assertEquals(-1L, evalL("(long 0xff)")); assertEquals(1L, evalL("(long 1)")); assertCVMEquals('a', eval("(char 97)")); assertEquals(97L, evalL("(long \\a)")); diff --git a/convex-core/src/test/java/convex/util/UtilsTest.java b/convex-core/src/test/java/convex/util/UtilsTest.java index ce0d09369..8dcd2a001 100644 --- a/convex-core/src/test/java/convex/util/UtilsTest.java +++ b/convex-core/src/test/java/convex/util/UtilsTest.java @@ -259,13 +259,15 @@ public void testReadWriteInt() { @Test public void testReadWriteLong() { byte[] bs = new byte[20]; - assertEquals(0, Utils.readLong(bs, 0)); + assertEquals(0, Utils.readLong(bs, 0,8)); + assertEquals(0, Utils.readLong(bs, 0,0)); + assertEquals(0, Utils.readLong(bs, 0,1)); long a = 0xffffffffcafebabeL; Utils.writeLong(bs, 0, a); - assertEquals(a, Utils.readLong(bs, 0)); + assertEquals(a, Utils.readLong(bs, 0,8)); Utils.writeLong(bs, 4, a); - assertEquals(0xffffffffffffffffL, Utils.readLong(bs, 0)); - assertEquals(0xcafebabe00000000L, Utils.readLong(bs, 8)); + assertEquals(0xffffffffffffffffL, Utils.readLong(bs, 0,8)); + assertEquals(0xcafebabe00000000L, Utils.readLong(bs, 8,8)); } @Test diff --git a/convex-java/src/main/java/convex/java/Convex.java b/convex-java/src/main/java/convex/java/Convex.java index 6d30a9aaa..825beb6fb 100644 --- a/convex-java/src/main/java/convex/java/Convex.java +++ b/convex-java/src/main/java/convex/java/Convex.java @@ -264,7 +264,7 @@ public Long queryBalance(Address address) { * @return Result of query, as parsed JSON Object from query response, or null if account does not exist */ public Map queryAccount(Address address) { - return queryAccount(address.toExactLong()); + return queryAccount(address.longValue()); } /** @@ -296,7 +296,7 @@ public Map queryAccount() { */ public Map faucet(Address address, long requestedAmount) { HashMap req=new HashMap<>(); - req.put("address", address.toExactLong()); + req.put("address", address.longValue()); req.put("amount", requestedAmount); String json=JSON.toPrettyString(req); @@ -309,7 +309,7 @@ public Map faucet(Address address, long requestedAmount) { * @return Result of query, as Future for parsed JSON Object from query response */ public CompletableFuture> queryAccountAsync(Address address) { - return doGetAsync(url+"/api/v1/accounts/"+address.toExactLong()); + return doGetAsync(url+"/api/v1/accounts/"+address.longValue()); } /** @@ -375,7 +375,7 @@ public synchronized CompletableFuture> transactAsync(String c private CompletableFuture> submitAsync(Hash hash) { ASignature sd=getKeyPair().sign(hash); HashMap req=new HashMap<>(); - req.put("address", getAddress().toExactLong()); + req.put("address", getAddress().longValue()); req.put("hash", hash.toHexString()); req.put("accountKey", getKeyPair().getAccountKey().toHexString()); req.put("sig", sd.toHexString()); @@ -390,7 +390,7 @@ private CompletableFuture> submitAsync(Hash hash) { * @return Future to be completed with result of query, as parsed JSON Object from query response */ public CompletableFuture> queryAsync(String code) { - String json=buildJsonQuery(address.toExactLong(),code); + String json=buildJsonQuery(address.longValue(),code); return doPostAsync(url+"/api/v1/query",json); } @@ -403,7 +403,7 @@ private String buildJsonQuery(Long a, String code) { } private String buildJsonQuery(Address a, String code) { - return buildJsonQuery((a==null)?null:a.toExactLong(),code); + return buildJsonQuery((a==null)?null:a.longValue(),code); } private Map doPost(String endPoint, String json) { diff --git a/convex-restapi/src/main/java/convex/restapi/api/ChainAPI.java b/convex-restapi/src/main/java/convex/restapi/api/ChainAPI.java index 4d0f1e404..f6bc51860 100644 --- a/convex-restapi/src/main/java/convex/restapi/api/ChainAPI.java +++ b/convex-restapi/src/main/java/convex/restapi/api/ChainAPI.java @@ -150,7 +150,7 @@ public void queryAccount(Context ctx) { // boolean isLibrary=as.getCallableFunctions().isEmpty(); HashMap hm=new HashMap<>(); - hm.put("address",addr.toExactLong()); + hm.put("address",addr.longValue()); hm.put("allowance",as.getMemory()); hm.put("balance",as.getBalance()); hm.put("memorySize",as.getMemorySize()); diff --git a/convex-restapi/src/test/java/convex/restapi/RemoteClientTest.java b/convex-restapi/src/test/java/convex/restapi/RemoteClientTest.java index b87e34304..c2b14eec3 100644 --- a/convex-restapi/src/test/java/convex/restapi/RemoteClientTest.java +++ b/convex-restapi/src/test/java/convex/restapi/RemoteClientTest.java @@ -79,7 +79,7 @@ public void testQuery() { // Query *address* Map res=c.query("*address*"); - assertEquals(addr.toExactLong(),res.get("value")); + assertEquals(addr.longValue(),res.get("value")); // Query *key* res=c.query(Symbols.STAR_KEY.toString()); @@ -96,7 +96,7 @@ public void testQueryAccount() { // Test values for basic new account Map res=c.queryAccount(); - assertEquals(addr.toExactLong(),res.get("address")); + assertEquals(addr.longValue(),res.get("address")); assertEquals(0L,res.get("balance")); assertEquals(0L,res.get("sequence")); assertEquals("user",res.get("type"));