Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package convex.api;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import convex.core.ErrorCodes;
import convex.core.Result;
import convex.core.SourceCodes;
import convex.core.State;
import convex.core.crypto.AKeyPair;
import convex.core.data.ABlob;
import convex.core.data.ACell;
import convex.core.data.AList;
import convex.core.data.AccountKey;
import convex.core.data.AccountStatus;
import convex.core.data.Address;
import convex.core.data.Cells;
import convex.core.data.Hash;
import convex.core.data.List;
import convex.core.data.Lists;
import convex.core.data.SignedData;
import convex.core.data.prim.CVMLong;
import convex.core.exceptions.ResultException;
import convex.core.lang.AOp;
import convex.core.lang.RT;
import convex.core.lang.Reader;
import convex.core.lang.Symbols;
import convex.core.lang.ops.Special;
import convex.core.store.AStore;
import convex.core.store.Stores;
import convex.core.transactions.ATransaction;
import convex.core.transactions.Invoke;
import convex.core.transactions.Transfer;
import convex.core.util.Utils;
import convex.net.Message;
import convex.net.ResultConsumer;
import convex.peer.Config;
import convex.peer.Server;
/**
* Class representing a client API to the Convex network.
*
* An instance of the type Convex represents a stateful client connection to the
* Convex network that can issue transactions both synchronously and
* asynchronously. This can be used by both peers and JVM-based clients.
*
* "I'm doing a (free) operating system (just a hobby, won't be big and
* professional like gnu)" - Linus Torvalds
*/
@SuppressWarnings("unused")
public abstract class Convex {
private static final Logger log = LoggerFactory.getLogger(Convex.class.getName());
protected long timeout = Config.DEFAULT_CLIENT_TIMEOUT;
/**
* Key pair for this Client
*/
protected AKeyPair keyPair;
/**
* Current Address for this Client
*/
protected Address address;
/**
* Determines if auto-sequencing should be attempted. Default to true.
*/
private boolean autoSequence = true;
/**
* Determines if transactions should be pre-compiled.
*/
protected boolean preCompile = false;
/**
* Sequence number for this client, or null if not yet known. Used to number new
* transactions if not otherwise specified.
*/
protected Long sequence = null;
/**
* Map of results awaiting completion.
*/
protected HashMap> awaiting = new HashMap<>();
/**
* Result Consumer for messages received back from a client connection
*/
protected final Consumer messageHandler = new ResultConsumer() {
@Override
protected synchronized void handleResult(long id, Result v) {
ACell ec=v.getErrorCode();
if ((ec!=null)&&(!SourceCodes.CODE.equals(v.getSource()))) {
// if our result didn't result in user code execution
// then we probably have a wrong sequence number now. Kill the stored value.
sequence = null;
}
}
@Override
public void accept(Message m) {
// Check if we are waiting for a Result with this ID for this connection
synchronized (awaiting) {
CVMLong id=m.getID();
CompletableFuture cf = (id==null)?null:awaiting.remove((Long)id.longValue());
if (cf != null) {
// log.info("Return message received for message ID: {} with type: {} "+m.toString(), id,m.getType());
if (cf.complete(m)) return;
}
}
if (delegatedHandler!=null) {
delegatedHandler.accept(m);
} else {
// default handling
super.accept(m);
}
}
};
private Consumer delegatedHandler=null;
protected Convex(Address address, AKeyPair keyPair) {
this.keyPair = keyPair;
this.address = address;
}
/**
* Creates an anonymous connection to a Peer, suitable for queries
*
* @param hostAddress Address of Peer
* @return New Convex client instance
* @throws IOException If IO Error occurs
* @throws TimeoutException If connection attempt times out
*/
public static ConvexRemote connect(InetSocketAddress hostAddress) throws IOException, TimeoutException {
return connect(hostAddress, (Address) null, (AKeyPair) null);
}
/**
* Create a Convex client by connecting to the specified Peer using the given
* key pair
*
* @param peerAddress Address of Peer
* @param address Address of Account to use for Client
* @param keyPair Key pair to use for client transactions
* @return New Convex client instance
* @throws IOException If connection fails due to IO error
* @throws TimeoutException If connection attempt times out
*/
public static ConvexRemote connect(InetSocketAddress peerAddress, Address address, AKeyPair keyPair)
throws IOException, TimeoutException {
return Convex.connect(peerAddress, address, keyPair, Stores.current());
}
/**
* Create a Convex client by connecting to the specified Peer using the given
* key pair and using a given store
*
* @param peerAddress Address of Peer
* @param address Address of Account to use for Client
* @param keyPair Key pair to use for client transactions
* @param store Store to use for this connection
* @return New Convex client instance
* @throws IOException If connection fails due to IO error
* @throws TimeoutException If connection attempt times out
*/
public static ConvexRemote connect(InetSocketAddress peerAddress, Address address, AKeyPair keyPair, AStore store)
throws IOException, TimeoutException {
ConvexRemote convex = new ConvexRemote(address, keyPair);
convex.connectToPeer(peerAddress, store);
return convex;
}
/**
* Sets the Address for this connection. This will be used for subsequent
* transactions and queries. User should also set a new keypair if a different
* keypair is required for the new Address.
*
* @param address Address to use
*/
public synchronized void setAddress(Address address) {
if (this.address == address)
return;
this.address = address;
// clear sequence, since we don't know the new account sequence number yet
sequence = null;
}
/**
* Sets the Address and Keypair for this connection. This will be used for
* subsequent transactions and queries.
*
* @param address Address to use
* @param kp Keypair to use for the given Address
*/
public synchronized void setAddress(Address address, AKeyPair kp) {
setAddress(address);
setKeyPair(kp);
}
public synchronized void setKeyPair(AKeyPair kp) {
this.keyPair = kp;
}
public void setNextSequence(long nextSequence) {
this.sequence = nextSequence - 1L;
}
/**
* Sets a handler for messages that are received but not otherwise processed (transaction/query results will
* be relayed instead to the appropriate handler )
* @param handler Handler for received messaged
*/
public void setHandler(Consumer handler) {
this.delegatedHandler = handler;
}
/**
* Gets the current sequence number for this Client, which is the sequence
* number of the last transaction observed for the current client's Account.
* Will attempt to acquire the sequence number from the network if not known.
*
* The next valid sequence number will be one higher than the result.
*
* @return Sequence number as a Long value (zero or positive)
* @throws ResultException If an error result occurs looking up sequence number
*/
public long getSequence() throws ResultException, InterruptedException {
if (sequence == null) {
sequence=lookupSequence(getAddress());
}
return sequence;
}
/**
* Gets the current sequence number for an account, which is the sequence
* number of the last transaction observed for the Account.
* Will attempt to acquire the sequence number from the network if not known.
*
* @param addr Address for which to query the sequence number
*
* @return Sequence number as a Long value (zero or positive)
*/
public long getSequence(Address addr) throws InterruptedException, ResultException {
if (Cells.equals(getAddress(), addr)) return getSequence();
return lookupSequence(addr);
}
/**
* Look up the sequence number for an account
* @param origin Account for which to check sequence
* @return Sequence number of account
* @throws ResultException If sequence number could not be obtained
*/
public long lookupSequence(Address origin) throws InterruptedException, ResultException {
AOp code= Special.forSymbol(Symbols.STAR_SEQUENCE);
Result r= querySync(code,origin);
if (r.isError()) throw new ResultException(r);
ACell rv=r.getValue();
if (!(rv instanceof CVMLong)) throw new ResultException(ErrorCodes.FORMAT,"Unexpected sequence result type: "+Utils.getClassName(rv));
long seq=((CVMLong)rv).longValue();
return seq;
}
/**
* Called after a transaction is submitted to update sequence (if possible)
* @param value
*/
protected void maybeUpdateSequence(SignedData signed) {
try {
ATransaction trans=signed.getValue();
if (!isAutoSequence()) return;
if (!Cells.equals(trans.getOrigin(),address)) return;
Long seq=this.sequence;
if (seq==null) return;
seq++;
if (seq==trans.getSequence()) sequence=seq;
} catch (Exception e) {
// do nothing. Shouldn't happen except in some adversarial test cases.
}
}
/**
* Signs a value on behalf of this client, using the currently assigned keypair.
*
* @param Type of value to sign
* @param value Value to sign
* @return SignedData instance
*/
public SignedData signData(T value) {
return keyPair.signData(value);
}
/**
* Creates a new account with the given public key
*
* @param publicKey Public key to set for the new account
* @return Address of account created
* @throws ResultException If account creation failed
*/
public Address createAccountSync(AccountKey publicKey) throws InterruptedException, ResultException {
Address address;
try {
address = createAccount(publicKey).get();
} catch (ExecutionException e) {
throw new ResultException(Result.fromException(e));
}
return address;
}
/**
* Creates a new account with the given public key
*
* @param publicKey Public key to set for the new account
* @return Address of account created
*/
public CompletableFuture createAccount(AccountKey publicKey) {
Invoke trans = Invoke.create(address, 0, Lists.of(Symbols.CREATE_ACCOUNT, publicKey));
CompletableFuture fr = transact(trans);
return fr.thenApply(r -> r.getValue());
}
/**
* Checks if this Convex client instance has an open connection.
*
* @return true if connected, false otherwise
*/
public abstract boolean isConnected();
/**
* Updates the given transaction to have the next sequence number.
*
* @param t Any transaction, for which the correct next sequence number is
* desired
* @return The updated transaction
* @throws ResultException if an error occurs trying to determine the next sequence number
*/
private synchronized long getNextSequence(ATransaction t) throws ResultException, InterruptedException {
if (sequence != null) {
// if already we know the next sequence number to be applied, set it
return sequence+1;
} else {
return getSequence()+1;
}
}
/**
* Pre-compiles code, compiling the given source to a CVM Op.
*
* @param code Code to execute
* @return A Future for the result of the compilation
*/
public CompletableFuture preCompile(ACell code) {
ACell compileStep=List.of(Symbols.COMPILE,List.of(Symbols.QUOTE,code));
return query(compileStep);
}
/**
* Submits a transaction to the Convex network, returning a future once the
* transaction has been successfully queued. Signs the transaction with the
* currently set key pair.
*
* Should be thread safe as long as multiple clients do not attempt to submit
* transactions for the same account concurrently.
*
* May block briefly if the send buffer is full.
*
* @param transaction Transaction to execute
* @return A Future for the result of the transaction
*/
public final synchronized CompletableFuture transact(ATransaction transaction) {
SignedData signed;
if (transaction==null) throw new IllegalArgumentException("null transaction");
try {
signed = prepareTransaction(transaction);
CompletableFuture r= transact(signed);
return r;
} catch (Exception e) {
// Note this handles InterruptedException correctly (maintains interrupt)
return CompletableFuture.completedFuture(Result.fromException(e));
}
}
/**
* Prepares a transaction for network submission
* - Pre-compiles if needed
* - Sets origin account to current address
* - Sets sequence number (if autosequencing is enabled)
* - Signs transaction with current key pair
*
* @param code Code to prepare as transaction
* @return Signed transaction ready to submit
* @throws ResultException If an error occurs preparing the transaction (e.g. failure to pre-compile)
*/
public SignedData prepareTransaction(ACell code) throws ResultException,InterruptedException {
ATransaction transaction;
if (code instanceof ATransaction) {
transaction=(ATransaction)code;
} else if (code instanceof SignedData) {
throw new IllegalArgumentException("Can't prepare transaction that is already signed");
} else {
if (isPreCompile() ) {
Result compResult=preCompile(code).join();
if (compResult.isError()) throw new ResultException(compResult);
code=compResult.getValue();
}
transaction=Invoke.create(address, ATransaction.UNKNOWN_SEQUENCE, code);
}
return prepareTransaction(transaction);
}
/**
* Prepares a transaction for network submission
* - Sets origin account if needed
* - Sets sequence number (if autosequencing is enabled)
* - Signs transaction with current key pair
*
* @param transaction Transaction to prepare
* @return Signed transaction ready to submit
*/
public SignedData prepareTransaction(ATransaction transaction) throws ResultException, InterruptedException {
Address origin=transaction.getOrigin();
if (origin == null) {
origin=address;
if (origin==null) {
// A transaction without an origin is definitely a problem. :NOBODY error detected at :CLIENT source
throw new ResultException(Result.error(ErrorCodes.NOBODY, "No address set for transaction").withSource(SourceCodes.CLIENT));
}
transaction = transaction.withOrigin(origin);
}
final long originalSeq=transaction.getSequence(); // zero or negative means autosequence
long seq=originalSeq;
if (autoSequence||(originalSeq<=0)) {
if (seq <= 0) {
// apply sequence if using expected address
if (Cells.equals(origin, address)) {
seq = getNextSequence(transaction);
}
}
// If local, update sequence number based on latest consensus state
Server s=getLocalServer();
if (s!=null) {
State state=s.getPeer().getConsensusState();
AccountStatus as=state.getAccount(origin);
if (as!=null) {
long expected=as.getSequence()+1;
if (expected>seq) {
seq=expected;
}
}
}
}
if (seq<=0) seq=lookupSequence(origin);
// Update sequence if any change required
if (seq!=originalSeq) {
transaction=transaction.withSequence(seq);
}
// Store updated sequence number, ready for next transaction
if (Cells.equals(origin, address)) {
this.sequence=seq;
}
SignedData signed = keyPair.signData(transaction);
return signed;
}
/**
* Executes a transaction, compiling the given source code as an Invoke.
*
* @param code Code to execute
* @return A Future for the result of the transaction
*/
public CompletableFuture transact(String code) {
return transact((ACell)Reader.read(code));
}
/**
* Executes a transaction, compiling the given source code as an Invoke.
*
* @param code Code to execute
* @return A Future for the result of the transaction
*/
public synchronized CompletableFuture transact(ACell code) {
if (code instanceof ATransaction) return transact((ATransaction)code);
if (isPreCompile()) {
return preCompile(code).thenCompose(r->{
if (r.isError()) return CompletableFuture.completedFuture(r);
ATransaction trans = Invoke.create(getAddress(), ATransaction.UNKNOWN_SEQUENCE, r.getValue());
return transact(trans);
});
} else {
ATransaction trans = Invoke.create(getAddress(), ATransaction.UNKNOWN_SEQUENCE, code);
return transact(trans);
}
}
private ACell buildCodeForm(String code) {
AList forms = Reader.readAll(code);
ACell form;
if (forms.count() == 1) {
form = forms.get(0);
} else {
form = forms.cons(Symbols.DO);
}
return form;
}
/**
* Executes a transaction, compiling the given source code as an Invoke.
*
* @param code Code to execute
* @return A Future for the result of the transaction
* @throws InterruptedException in case of interrupt while waiting
*/
public synchronized Result transactSync(String code) throws InterruptedException {
ATransaction trans = Invoke.create(getAddress(), ATransaction.UNKNOWN_SEQUENCE, code);
return transactSync(trans);
}
/**
* Submits a signed transaction to the Convex network, returning a Future once
* the transaction has been successfully queued.
*
* Updates cached sequence number on best effort basis.
*
* @param signedTransaction Signed transaction to execute
* @return A Future for the result of the transaction
*/
public abstract CompletableFuture transact(SignedData signedTransaction);
/**
* Submits a transfer transaction to the Convex network, returning a future once
* the transaction has been successfully queued.
*
* @param target Destination address for transfer
* @param amount Amount of Convex Coins to transfer
* @return A Future for the result of the transaction
*/
public CompletableFuture transfer(Address target, long amount) {
ATransaction trans = Transfer.create(getAddress(), ATransaction.UNKNOWN_SEQUENCE, target, amount);
return transact(trans);
}
/**
* Submits a transfer transaction to the Convex network peer, and waits for
* confirmation of the result
*
* @param target Destination address for transfer
* @param amount Amount of Convex Coins to transfer
* @return Result of the transaction
* @throws InterruptedException In case of interrupt
*/
public Result transferSync(Address target, long amount) throws InterruptedException {
ATransaction trans = Transfer.create(getAddress(), 0, target, amount);
return transactSync(trans);
}
/**
* Submits a transaction synchronously to the Convex network, returning a Result
*
* @param transaction Transaction to execute
* @return The result of the transaction
* @throws InterruptedException in case of interrupt
*/
public Result transactSync(SignedData transaction) throws InterruptedException {
return transactSync(transaction, timeout);
}
/**
* Submits a transaction synchronously to the Convex network, returning a Result
*
* @param transaction Transaction to execute
* @return The result of the transaction (may be an error)
* @throws InterruptedException in case of interrupt
*/
public final Result transactSync(ACell transaction) throws InterruptedException {
return transactSync(transaction, timeout);
}
/**
* Submits a signed transaction synchronously to the Convex network, returning a
* Result
*
* @param transaction Transaction to execute
* @param timeout Number of milliseconds for timeout
* @return The result of the transaction
* @throws InterruptedException if operation is interrupted
*/
public final synchronized Result transactSync(ACell transaction, long timeout) throws InterruptedException {
// sample time at start of transaction attempt
long start = Utils.getTimeMillis();
Result result;
Future cf = transact(transaction);
// adjust timeout if time elapsed to submit transaction
long now = Utils.getTimeMillis();
timeout = Math.max(0L, timeout - (now - start));
try {
result = cf.get(timeout, TimeUnit.MILLISECONDS);
if (result.getErrorCode()!=null) {
// On error, clear cached sequence, it is possibly invalid
sequence=null;
}
return result;
} catch (ExecutionException | TimeoutException e) {
return Result.fromException(e);
}
}
/**
* Submits a signed transaction synchronously to the Convex network, returning a
* Result
*
* @param signedTransaction Transaction to execute
* @param timeout Number of milliseconds for timeout
* @return The Result of the transaction, may be an error
* @throws InterruptedException if operation is interrupted
*/
public Result transactSync(SignedData signedTransaction, long timeout)
throws InterruptedException {
// sample time at start of transaction attempt
long start = Utils.getTimeMillis();
Result result;
Future cf = transact(signedTransaction);
// adjust timeout if time elapsed to submit transaction
long now = Utils.getTimeMillis();
timeout = Math.max(0L, timeout - (now - start));
try {
result = cf.get(timeout, TimeUnit.MILLISECONDS);
return result;
} catch (ExecutionException | TimeoutException e) {
return Result.fromException(e);
} finally {
cf.cancel(true);
}
}
/**
* Submits a query to the Convex network, returning a Future once the query has
* been successfully queued.
*
* @param query Query to execute, as a Form or Op
* @return A Future for the result of the query
*/
public CompletableFuture query(ACell query) {
return query(query, getAddress());
}
/**
* Submits a query to the Convex network, returning a Future once the query has
* been successfully queued.
*
* @param query Query to execute, as String containing one or more forms
* @return A Future for the result of the query
*/
public CompletableFuture query(String query) {
ACell form = buildCodeForm(query);
return query(form, getAddress());
}
/**
* Attempts to resolve a CNS name
*
* @param cnsName CNS name to resolve
* @return A Future for the resolved CNS value
*/
public CompletableFuture resolve(String cnsName) {
ACell form = buildCodeForm("(import "+cnsName+")");
return query(form).thenApply(r->{
if (r.isError()) throw new RuntimeException("Resolve failed "+r);
return r.getValue();
});
}
/**
* Attempts to asynchronously acquire a complete persistent data structure for the given hash
* from the remote peer. Uses the current store configured for the calling
* thread.
*
* @param hash Hash of value to acquire.
*
* @return Future for the cell being acquired
*/
public CompletableFuture acquire(Hash hash) {
return acquire(hash, Stores.current());
}
/**
* Attempts to acquire a complete persistent data structure for the given hash
* from the connected peer. Uses the store provided as a destination.
*
* @param hash Hash of value to acquire.
* @param store Store to acquire the persistent data to.
*
* @return Future for the Cell being acquired. May fail exceptionally or timeout
* if the given data cannot be acquired (most likely missing from the
* peer's store)
*/
public abstract CompletableFuture acquire(Hash hash, AStore store);
/**
* Request status using a sync operation. This request will automatically get
* any missing data with the status request
*
* @param timeoutMillis Milliseconds to wait for request timeout
* @return Status Vector from target Peer
*
*/
public Result requestStatusSync(long timeoutMillis) {
CompletableFuture statusFuture = requestStatus();
return statusFuture.join();
}
/**
* Submits a status request to the Convex network peer, returning a Future once
* the request has been successfully queued.
*
* @return A Future for the result of the requestStatus
*/
public abstract CompletableFuture requestStatus();
/**
* Method to start waiting for a complete result. Must be called with lock on
* `awaiting` map to prevent risk of missing results before it is called.
*
* @param id ID of result message to await
* @return
*/
protected CompletableFuture awaitResult(long id, long timeout) {
CompletableFuture cf = new CompletableFuture();
if (timeout>0) {
cf=cf.orTimeout(timeout, TimeUnit.MILLISECONDS);
}
CompletableFuture cr=cf.handle((m,e)->{
synchronized(awaiting) {
awaiting.remove(id);
}
// clear sequence if something went wrong. It is probably invalid now....
if (e!=null) {
sequence=null;
return Result.fromException(e);
}
Result r=m.toResult();
if (r.getErrorCode()!=null) {
sequence=null;
}
return r;
});
awaiting.put(id, cf);
return cr;
}
/**
* Request a challenge. This is request is made by any peer that needs to find
* out if another peer can be trusted.
*
* @param data Signed data to send to the peer for the challenge.
* @return A Future for the result of the requestChallenge
*/
public abstract CompletableFuture requestChallenge(SignedData data);
/**
* Submits a query to the Convex network, returning a Future once the query has
* been successfully queued.
*
* @param query Query to execute, as a Form or Op
* @param address Address to use for the query
* @return A Future for the result of the query
*/
public abstract CompletableFuture query(ACell query, Address address);
/**
* Executes a query synchronously and waits for the Result
*
* @param query Query to execute. Map be a form or Op
* @return Result of synchronous query
* @throws InterruptedException In case of interrupt
*/
public Result querySync(ACell query) throws InterruptedException {
return querySync(query, getAddress());
}
/**
* Executes a query synchronously and waits for the Result
*
* @param query Query to execute, as a String that contains one or more readable
* forms. Multiple forms will be wrapped in a `do` block
* @return Result of synchronous query
* @throws InterruptedException In case of interrupt while awaiting Result
*/
public Result querySync(String query) throws InterruptedException {
ACell form = buildCodeForm(query);
return querySync(form, getAddress());
}
/**
* Executes a query synchronously and waits for the Result
*
* @param address Address to use for the query
* @param query Query to execute, as a Form or Op
* @return Result of query
* @throws InterruptedException In case of interrupt while awaiting Result
*/
public Result querySync(ACell query, Address address) throws InterruptedException {
return querySync(query, address, timeout);
}
/**
* Executes a query synchronously and waits for the Result
*
* @param timeoutMillis Timeout to wait for query result. Will throw
* TimeoutException if not received in this time
* @param address Address to use for the query
* @param query Query to execute, as a Form or Op
* @return Result of query
*/
protected Result querySync(ACell query, Address address, long timeoutMillis) throws InterruptedException {
Future cf = query(query, address);
Result result;
try {
result = cf.get(timeoutMillis, TimeUnit.MILLISECONDS);
} catch (ExecutionException | TimeoutException e) {
return Result.fromException(e.getCause());
} finally {
cf.cancel(true);
}
return result;
}
/**
* Returns the current AccountKey for the client using the API.
*
* @return AcountKey instance, or null if no keypair is set
*/
public AccountKey getAccountKey() {
if (keyPair==null) return null;
return keyPair.getAccountKey();
}
/**
* Returns the current AccountKey for the specified address. Performs a sync query
*
* @return AcountKey instance, or null if unavailable
* @throws InterruptedException In case of interrupt while awaiting Result
*/
public AccountKey getAccountKey(Address a) throws InterruptedException {
if (a==null) return null;
Result r=querySync(Reader.read("(:key (account "+a+"))"));
if (r.isError()) return null;
ABlob b=RT.ensureBlob(r.getValue());
return AccountKey.create(b);
}
/**
* Returns the current Address for the client using the API.
*
* @return Address instance
*/
public Address getAddress() {
return address;
}
/**
* Disconnects the client from the network, releasing any connection resources.
*/
public abstract void close();
@Override
public void finalize() {
close();
}
/**
* Determines if this Client is configured to automatically generate sequence
* numbers
*
* @return
*/
protected boolean isAutoSequence() {
return autoSequence;
}
/**
* Configures auto-generation of sequence numbers
*
* @param autoSequence true to enable auto-sequencing, false otherwise
*/
protected void setAutoSequence(boolean autoSequence) {
this.autoSequence = autoSequence;
}
/**
* Query the balance for the current account
* @return Long balance in Convex coins,
* @throws ResultException If balance query fails
*/
public Long getBalance() throws ResultException {
Address a = getAddress();
if (a==null) throw new IllegalStateException("No address set for balance query");
return getBalance(a);
}
public Long getBalance(Address address) throws ResultException {
try {
ACell code;
if (Utils.equals(this.address, address)) {
code=Special.forSymbol(Symbols.STAR_BALANCE);
} else {
code=Lists.of(Symbols.BALANCE, address);
}
Future future = query(code);
Result result = future.get(timeout, TimeUnit.MILLISECONDS);
if (result.isError()) {
return null;
}
CVMLong bal = (CVMLong) result.getValue();
return bal.longValue();
} catch (Exception ex) {
// Note InterruptedException correctly handled here
throw new ResultException(ex);
}
}
/**
* Connect to a local Server, using given address and keypair
*
* @param server Server to connect to
* @param address Address to use
* @param keyPair Keypair to use
* @return New Client Connection
*/
public static ConvexLocal connect(Server server, Address address, AKeyPair keyPair) {
return ConvexLocal.create(server, address, keyPair);
}
/**
* Connect to a local Server, with no address and keypair set
*
* @param server Server to connect to
* @return New Client Connection
*/
public static ConvexLocal connect(Server server) {
return ConvexLocal.create(server, null, null);
}
/**
* Gets the consensus state from the remote Peer
*
* @return Future for consensus state
* @throws TimeoutException If initial status request times out
* @throws InterruptedException In case of interrupt while awaiting Result
*/
public abstract CompletableFuture acquireState() throws TimeoutException, InterruptedException;
/**
* Sets the default timeout for this Convex client instance.
* @param timeout timeout in milliseconds
*/
public void setTimeout(long timeout) {
this.timeout=timeout;
}
@Override
public abstract String toString();
/**
* Get the keypair for this Convex connection.
* @return Keypair or null if not set
*/
public AKeyPair getKeyPair() {
return keyPair;
}
/**
* Gets the local Server instance, or null if not a local connection
* @return Server instance (or null)
*/
public abstract Server getLocalServer();
/**
* Gets the remote address for this Convex client instance
* @return Socket address
*/
public abstract InetSocketAddress getHostAddress();
/**
* @return True if pre-compilation is enabled
*/
public boolean isPreCompile() {
return preCompile;
}
/**
* Sets the client connection pre-compilation mode.
*
* When enabled, any non-compiled CVM code is sent to the peer for compilation first (as a query)
*
* SECURITY: do not use if the target peer is untrusted, as it may be able to manipulate code during compilation. May be
* safe if the peer and the connection to it is trusted (e.g. a local peer)
*
* @param preCompile the preCompile to set
*/
public void setPreCompile(boolean preCompile) {
this.preCompile = preCompile;
}
/**
* Clears the sequence number cache for this client instance. Sequence number will be re-queried if required.
*/
public void clearSequence() {
this.sequence=null;
}
}