Skip to content

Commit

Permalink
Result handling updates to give :source
Browse files Browse the repository at this point in the history
  • Loading branch information
mikera committed Aug 26, 2024
1 parent 6b31a6a commit 8e82fb1
Show file tree
Hide file tree
Showing 8 changed files with 66 additions and 10 deletions.
4 changes: 3 additions & 1 deletion convex-core/src/main/java/convex/core/Result.java
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ public static Result fromContext(CVMLong id,ResultContext rc) {
if (rc.memUsed>0) info=info.assoc(Keywords.MEM, CVMLong.create(rc.memUsed));
if (rc.totalFees>0) info=info.assoc(Keywords.FEES, CVMLong.create(rc.totalFees));
if (rc.juiceUsed>0) info=info.assoc(Keywords.JUICE, CVMLong.create(rc.juiceUsed));
if (rc.source!=null) info=info.assoc(Keywords.SOURCE, rc.source);

return create(id,(ACell)result,errorCode,info);
}
Expand All @@ -346,6 +347,7 @@ public Result withExtraInfo(Map<Keyword,ACell> extInfo) {
*/
public static Result fromContext(Context ctx) {
ACell rval=(ctx.isExceptional())?ctx.getExceptional().getMessage():ctx.getResult();

return create(null,rval,ctx.getErrorCode(),null);
}

Expand Down Expand Up @@ -398,7 +400,7 @@ public static Result fromException(Throwable e) {
private static final Result INTERRUPTED_RESULT=Result.error(ErrorCodes.INTERRUPTED,Strings.create("Interrupted!"));

/**
* Returns a Result representing a thread interrup, AND sets the interrupt status on the current thread
* Returns a Result representing a thread interrupt, AND sets the interrupt status on the current thread
* @return
*/
public static Result interruptThread() {
Expand Down
12 changes: 11 additions & 1 deletion convex-core/src/main/java/convex/core/ResultContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import convex.core.transactions.ATransaction;

/**
* Class for preparation of transaction results
* Class for preparation of transaction results from the CVM
*
* Mutable so that results can be accumulated efficiently as processing proceeds
*/
Expand All @@ -20,6 +20,7 @@ public class ResultContext {
public Context context=null;
public long totalFees=0;
public long juiceUsed=0;
public Keyword source=null;

public ResultContext(ATransaction transaction, long juicePrice) {
this.juicePrice=juicePrice;
Expand Down Expand Up @@ -85,4 +86,13 @@ public boolean isError() {
return context.isError();
}

public ResultContext withSource(Keyword sourceCode) {
this.source=sourceCode;
return this;
}

public Keyword getSource() {
return source;
}

}
17 changes: 13 additions & 4 deletions convex-core/src/main/java/convex/core/State.java
Original file line number Diff line number Diff line change
Expand Up @@ -511,21 +511,29 @@ public ResultContext applyTransaction(SignedData<? extends ATransaction> signedT
Address addr=t.getOrigin();
AccountStatus as = getAccount(addr);
if (as==null) {
return ResultContext.error(this,ErrorCodes.NOBODY,"Transaction for non-existent Account: "+addr);
ResultContext rc=ResultContext.error(this,ErrorCodes.NOBODY,"Transaction for non-existent Account: "+addr);
return rc.withSource(SourceCodes.CVM);
} else {

// Update sequence number for target account
long sequence=t.getSequence();
long expectedSequence=as.getSequence()+1;
if (sequence!=expectedSequence) {
return ResultContext.error(this,ErrorCodes.SEQUENCE, "Sequence = "+sequence+" but expected "+expectedSequence);
ResultContext rc=ResultContext.error(this,ErrorCodes.SEQUENCE, "Sequence = "+sequence+" but expected "+expectedSequence);
return rc.withSource(SourceCodes.CVM);
}

AccountKey key=as.getAccountKey();
if (key==null) return ResultContext.error(this,ErrorCodes.STATE,"Transaction for account that is an Actor: "+addr);
if (key==null) {
ResultContext rc= ResultContext.error(this,ErrorCodes.STATE,"Transaction for account that is an Actor: "+addr);
return rc.withSource(SourceCodes.CVM);
}

boolean sigValid=signedTransaction.checkSignature(key);
if (!sigValid) return ResultContext.error(this,ErrorCodes.SIGNATURE, Strings.BAD_SIGNATURE);
if (!sigValid) {
ResultContext rc= ResultContext.error(this,ErrorCodes.SIGNATURE, Strings.BAD_SIGNATURE);
return rc.withSource(SourceCodes.CVM);
}
}

ResultContext ctx=applyTransaction(t);
Expand Down Expand Up @@ -575,6 +583,7 @@ public ResultContext applyTransaction(ATransaction t) {
// - Non-existent Origin account
// - Bad sequence number
// Return context with no change, i.e. before executing the transaction
rc.source=SourceCodes.CVM;
}

return rc.withContext(ctx);
Expand Down
7 changes: 7 additions & 0 deletions convex-core/src/main/java/convex/core/lang/Context.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import convex.core.Constants;
import convex.core.ErrorCodes;
import convex.core.ResultContext;
import convex.core.SourceCodes;
import convex.core.State;
import convex.core.data.ACell;
import convex.core.data.AHashMap;
Expand Down Expand Up @@ -352,6 +353,7 @@ public Context completeTransaction(State initialState, ResultContext rc) {
// consume whole balance
juiceFees=balance;
} else if (!rc.context.isExceptional()) {
// Transaction appears to have succeeded, and will do unless memory accounting fails
// do memory accounting as long as we didn't fail for any other reason
// compute memory delta (memUsed) and store in ResultContext
long memUsed=state.getMemorySize()-initialState.getMemorySize();
Expand Down Expand Up @@ -389,6 +391,9 @@ public Context completeTransaction(State initialState, ResultContext rc) {
long allowanceCredit=-memUsed;
account=account.withMemory(allowance+allowanceCredit);
}
} else {
// Transaction failed in user code
rc.source=SourceCodes.CODE;
}

// Compute total fees
Expand All @@ -413,8 +418,10 @@ public Context completeTransaction(State initialState, ResultContext rc) {
Context rctx=this.withState(state);
if (juiceFailure) {
rctx=rctx.withError(ErrorCodes.JUICE, "Insuffienct balance to cover juice fees of "+rc.getJuiceFees());
rc.source=SourceCodes.CVM;
} else if (memoryFailure) {
rctx=rctx.withError(ErrorCodes.MEMORY, "Unable to allocate additional memory required for transaction ("+rc.memUsed+" bytes)");
rc.source=SourceCodes.CVM;
}
return rctx;
}
Expand Down
16 changes: 16 additions & 0 deletions convex-core/src/test/java/convex/core/TransactionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,10 @@ public void testBadSequence() throws BadSignatureException {
Context ctx=rc.context;
assertEquals(ErrorCodes.SEQUENCE,ctx.getError().getCode());

// check source correctly identified
assertEquals(SourceCodes.CVM, rc.source);


// Sequence number in state should be unchanged
assertEquals(0L,ctx.getAccountStatus(HERO).getSequence());

Expand Down Expand Up @@ -285,6 +289,18 @@ private void checkNoTransactionEffects(State s, ResultContext rc) {

assertEquals(0,ctx.getJuiceUsed());
}

@Test
public void testJuiceFail() {
State s=state();
AccountStatus as=s.getAccount(HERO);
long SEQ=as.getSequence()+1;
ResultContext rc=s.applyTransaction(HERO_KP.signData(Invoke.create(HERO, SEQ,"(loop [] (def a 2) (recur))")));
assertEquals(ErrorCodes.JUICE,rc.getErrorCode());
assertEquals(SourceCodes.CVM,rc.getSource());
checkNoTransactionEffects(s,rc);

}

@Test public void testBigValues() {
// Checks in case there are oddities with big values / VLC encoding
Expand Down
3 changes: 2 additions & 1 deletion convex-peer/src/main/java/convex/peer/QueryHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import convex.core.Result;
import convex.core.ResultContext;
import convex.core.SourceCodes;
import convex.core.data.ACell;
import convex.core.data.AVector;
import convex.core.data.Address;
Expand Down Expand Up @@ -78,7 +79,7 @@ private void handleQuery(Message m) {
ResultContext resultContext = server.getPeer().executeQuery(form, address);

// Report result back to message sender
boolean resultReturned= m.returnResult(Result.fromContext(id, resultContext));
boolean resultReturned= m.returnResult(Result.fromContext(id, resultContext).withSource(SourceCodes.PEER));

if (!resultReturned) {
log.warn("Failed to send query result back to client with ID: {}", id);
Expand Down
6 changes: 3 additions & 3 deletions convex-peer/src/main/java/convex/peer/TransactionHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import convex.core.ErrorCodes;
import convex.core.Peer;
import convex.core.Result;
import convex.core.SourceCodes;
import convex.core.State;
import convex.core.data.ACell;
import convex.core.data.AString;
Expand Down Expand Up @@ -117,7 +118,7 @@ protected void processMessage(Message m) {
// Check our transaction is valid and we want to process it
Result error=checkTransaction(sd);
if (error!=null) {
m.returnResult(error);
m.returnResult(error.withSource(SourceCodes.PEER));
return;
}

Expand Down Expand Up @@ -173,8 +174,7 @@ private Result checkTransaction(SignedData<ATransaction> sd) {
return Result.error(ErrorCodes.SIGNATURE, Strings.BAD_SIGNATURE);
}
} catch (Exception e) {
log.warn("Unexpected exception while checking transaction",e);
return Result.error(ErrorCodes.UNEXPECTED, Strings.BAD_SIGNATURE);
return Result.fromException(e);
}
// All checks passed OK!
return null;
Expand Down
11 changes: 11 additions & 0 deletions convex-peer/src/test/java/convex/api/ConvexRemoteTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import convex.core.ErrorCodes;
import convex.core.Result;
import convex.core.SourceCodes;
import convex.core.crypto.AKeyPair;
import convex.core.crypto.Ed25519Signature;
import convex.core.data.Address;
Expand Down Expand Up @@ -107,6 +108,16 @@ public void testBadSignature() throws IOException, TimeoutException, Interrupted
}
}

@Test
public void testNobody() throws IOException, TimeoutException, InterruptedException, ExecutionException {
synchronized (network.SERVER) {
Convex convex = Convex.connect(network.SERVER.getHostAddress(), Address.create(666666), KEYPAIR);
Result r = convex.transact(CVMLong.ONE).get();
assertEquals(ErrorCodes.NOBODY, r.getErrorCode());
assertEquals(SourceCodes.PEER, r.getSource());
}
}

@Test
public void testBadSequence() throws IOException, TimeoutException, InterruptedException, ExecutionException {
synchronized (network.SERVER) {
Expand Down

0 comments on commit 8e82fb1

Please sign in to comment.