Skip to content

Commit

Permalink
Add ConvexDirect API class for testing / local usage
Browse files Browse the repository at this point in the history
  • Loading branch information
mikera committed Dec 30, 2024
1 parent 2459826 commit c96ee6e
Show file tree
Hide file tree
Showing 7 changed files with 224 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ Index<AccountKey, SignedData<Order>> vote( final Index<AccountKey, SignedData<Or
// winning chain should have same consensus as my initial chain
Order winningOrder = myOrder.withBlocks(winningBlocks);

final Order consensusOrder = updateConsensus(winningOrder,stakedOrders, totalStake);
Order consensusOrder = updateConsensus(winningOrder,stakedOrders, totalStake);

Index<AccountKey, SignedData<Order>> resultOrders = filteredOrders;
if (!consensusOrder.consensusEquals(myOrder)) {
Expand Down
41 changes: 41 additions & 0 deletions convex-core/src/main/java/convex/core/cvm/Peer.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@
import convex.core.data.Maps;
import convex.core.data.Ref;
import convex.core.data.SignedData;
import convex.core.data.Strings;
import convex.core.data.Vectors;
import convex.core.data.prim.CVMLong;
import convex.core.exceptions.InvalidDataException;
import convex.core.init.Init;
import convex.core.lang.RT;
import convex.core.store.AStore;
import convex.core.store.Stores;
import convex.core.util.Utils;
Expand Down Expand Up @@ -608,6 +610,45 @@ public Peer proposeBlock(Block block) {
// result=result.updateState();
return result;
}

public Result checkTransaction(SignedData<ATransaction> sd) {

// TODO: throttle?
ATransaction tx=RT.ensureTransaction(sd.getValue());

// System.out.println("transact: "+v);
if (tx==null) {
return Result.error(ErrorCodes.FORMAT,Strings.BAD_FORMAT);
}

State s=getConsensusState();
AccountStatus as=s.getAccount(tx.getOrigin());
if (as==null) {
return Result.error(ErrorCodes.NOBODY, Strings.NO_SUCH_ACCOUNT);
}

if (tx.getSequence()<=as.getSequence()) {
return Result.error(ErrorCodes.SEQUENCE, Strings.OLD_SEQUENCE);
}

AccountKey expectedKey=as.getAccountKey();
if (expectedKey==null) {
return Result.error(ErrorCodes.STATE, Strings.NO_TX_FOR_ACTOR);
}

AccountKey pubKey=sd.getAccountKey();
if (!expectedKey.equals(pubKey)) {
return Result.error(ErrorCodes.SIGNATURE, Strings.WRONG_KEY );
}

if (!sd.checkSignature()) {
// SECURITY: Client tried to send a badly signed transaction!
return Result.error(ErrorCodes.SIGNATURE, Strings.BAD_SIGNATURE);
}

// All checks passed OK!
return null;
}

/**
* Gets the Final Point for this Peer
Expand Down
3 changes: 1 addition & 2 deletions convex-core/src/main/java/convex/core/init/Init.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package convex.core.init;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import convex.core.Coin;
Expand Down Expand Up @@ -391,7 +390,7 @@ private static State addStandardLibraries(State s) {
* @param peerKeys
* @return
*/
public static State createTestState(ArrayList<AccountKey> peerKeys) {
public static State createTestState(List<AccountKey> peerKeys) {
State s=createState(peerKeys);
s = doActorDeploy(s, "/convex/asset/nft/tokens.cvx");
s = doActorDeploy(s, "/convex/lab/play.cvx");
Expand Down
1 change: 0 additions & 1 deletion convex-peer/src/main/java/convex/api/Convex.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@
import convex.core.store.Stores;
import convex.core.util.Utils;
import convex.net.IPUtils;
import convex.net.ResultConsumer;
import convex.peer.Config;
import convex.peer.Server;

Expand Down
140 changes: 140 additions & 0 deletions convex-peer/src/main/java/convex/api/ConvexDirect.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package convex.api;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeoutException;

import convex.core.Result;
import convex.core.ResultContext;
import convex.core.cpos.Block;
import convex.core.crypto.AKeyPair;
import convex.core.cvm.Address;
import convex.core.cvm.Peer;
import convex.core.cvm.PeerStatus;
import convex.core.cvm.State;
import convex.core.cvm.transactions.ATransaction;
import convex.core.data.ACell;
import convex.core.data.AccountKey;
import convex.core.data.Blob;
import convex.core.data.Hash;
import convex.core.data.SignedData;
import convex.core.data.prim.CVMLong;
import convex.core.message.Message;
import convex.core.store.AStore;
import convex.core.util.Utils;

/**
* Convex API instance that directly interacts with a Peer instance
*/
public class ConvexDirect extends Convex {

protected Peer peer;
private boolean isConnected=true;

protected ConvexDirect(Address address, AKeyPair keyPair, Peer initial) {
super(address, keyPair);
this.peer=initial;
}

public static ConvexDirect create(AKeyPair peerKey,State state) {
AccountKey key=peerKey.getAccountKey();
PeerStatus ps= state.getPeer(key);
if (ps==null) throw new IllegalStateException("Peer does not exist in desired state");
Address cont=ps.getController();
return new ConvexDirect(cont,peerKey,Peer.create(peerKey, state));
}

@Override
public boolean isConnected() {
return isConnected;
}

@Override
public synchronized CompletableFuture<Result> transact(SignedData<ATransaction> signedTransaction) {
try {
CVMLong id=CVMLong.create(getNextID());
Peer p=peer;
Result failure=p.checkTransaction(signedTransaction);
if (failure!=null) {
return CompletableFuture.completedFuture(failure.withID(id));
}

long ts=Utils.getCurrentTimestamp();
Block block=Block.of(ts, signedTransaction);

// Peer updates
p=p.updateTimestamp(ts);
p=p.proposeBlock(block);
p=p.mergeBeliefs();
p=p.updateState();
long blockNum=p.getPeerOrder().count()-1;
peer=p;

Result result= p.getResult(blockNum, 0);
return CompletableFuture.completedFuture(result.withID(id));
} catch (Exception e) {
return CompletableFuture.completedFuture(Result.fromException(e));
}
}

@Override
public CompletableFuture<Result> messageRaw(Blob message) {
// TODO Auto-generated method stub
return null;
}

@Override
public CompletableFuture<Result> message(Message message) {
// TODO Auto-generated method stub
return null;
}

@Override
public <T extends ACell> CompletableFuture<T> acquire(Hash hash, AStore store) {
// TODO Auto-generated method stub
return null;
}

@Override
public CompletableFuture<Result> requestStatus() {
// TODO Auto-generated method stub
return null;
}

@Override
public CompletableFuture<Result> requestChallenge(SignedData<ACell> data) {
// TODO Auto-generated method stub
return null;
}

@Override
public CompletableFuture<Result> query(ACell query, Address address) {
CVMLong id=CVMLong.create(getNextID());
ResultContext rc= peer.executeQuery(query, address);
return CompletableFuture.completedFuture(Result.fromContext(id,rc));
}

@Override
public void close() {
peer=null;
}

@Override
public String toString() {
return "Direct client with peer state: "+peer.getConsensusState().getHash();
}

@Override
public InetSocketAddress getHostAddress() {
return null;
}

@Override
public void reconnect() throws IOException, TimeoutException, InterruptedException {
isConnected=true;
}



}
40 changes: 1 addition & 39 deletions convex-peer/src/main/java/convex/peer/TransactionHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
import convex.core.data.prim.CVMLong;
import convex.core.exceptions.BadFormatException;
import convex.core.exceptions.MissingDataException;
import convex.core.lang.RT;
import convex.core.lang.Reader;
import convex.core.message.Message;
import convex.core.util.LoadMonitor;
Expand Down Expand Up @@ -156,7 +155,7 @@ protected void processMessage(Message m) throws InterruptedException {
SignedData<ATransaction> sd = (SignedData<ATransaction>) v.get(2);

// Check our transaction is valid and we want to process it
Result error=checkTransaction(sd);
Result error=server.getPeer().checkTransaction(sd);
if (error!=null) {
m.returnResult(error.withSource(SourceCodes.PEER));
return;
Expand All @@ -179,44 +178,7 @@ protected void processMessage(Message m) throws InterruptedException {
}
}

private Result checkTransaction(SignedData<ATransaction> sd) {

// TODO: throttle?
ATransaction tx=RT.ensureTransaction(sd.getValue());

// System.out.println("transact: "+v);
if (tx==null) {
return Result.error(ErrorCodes.FORMAT,Strings.BAD_FORMAT);
}

State s=server.getPeer().getConsensusState();
AccountStatus as=s.getAccount(tx.getOrigin());
if (as==null) {
return Result.error(ErrorCodes.NOBODY, Strings.NO_SUCH_ACCOUNT);
}

if (tx.getSequence()<=as.getSequence()) {
return Result.error(ErrorCodes.SEQUENCE, Strings.OLD_SEQUENCE);
}

AccountKey expectedKey=as.getAccountKey();
if (expectedKey==null) {
return Result.error(ErrorCodes.STATE, Strings.NO_TX_FOR_ACTOR);
}

AccountKey pubKey=sd.getAccountKey();
if (!expectedKey.equals(pubKey)) {
return Result.error(ErrorCodes.SIGNATURE, Strings.WRONG_KEY );
}

if (!sd.checkSignature()) {
// SECURITY: Client tried to send a badly signed transaction!
return Result.error(ErrorCodes.SIGNATURE, Strings.BAD_SIGNATURE);
}

// All checks passed OK!
return null;
}

/**
* Sets a request observer, which will be called whenever the Peer
Expand Down
40 changes: 40 additions & 0 deletions convex-peer/src/test/java/convex/api/ConvexDirectTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package convex.api;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.List;

import org.junit.jupiter.api.Test;

import convex.core.Result;
import convex.core.crypto.AKeyPair;
import convex.core.cvm.Address;
import convex.core.cvm.State;
import convex.core.data.prim.CVMLong;
import convex.core.init.Init;
import convex.core.init.InitTest;
import convex.core.lang.ACVMTest;

/**
* Tests for Convex Direct client
*/
public class ConvexDirectTest extends ACVMTest {
static final AKeyPair peerKey=AKeyPair.createSeeded(5675675);

@Test public void testSetup() throws InterruptedException {
State state=Init.createTestState(List.of(peerKey.getAccountKey()));
ConvexDirect convex=ConvexDirect.create(peerKey,state);
Address addr=convex.getAddress();

assertTrue(convex.isConnected());
assertEquals(InitTest.FIRST_PEER_ADDRESS,addr);

assertEquals(addr,convex.query("*address*").join().getValue());

Result r=convex.transactSync("(+ 1 2)");
assertFalse(r.isError(),()->"Expected ed error: "+r);
assertEquals(CVMLong.create(3),r.getValue());
}
}

0 comments on commit c96ee6e

Please sign in to comment.