Skip to content

Commit

Permalink
Merge branch 'release/1.10.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
bricerisingalgorand committed Oct 12, 2021
2 parents 1baf04f + c8f60e9 commit cda8a83
Show file tree
Hide file tree
Showing 13 changed files with 893 additions and 71 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# 1.10.0
- Feature/sign rekey lsig msig (#250)
- Support parsing msgpack with numeric key (#262)

# 1.9.0
- Support AVM 1.0
- Update TEAL langspec to v5
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Maven:
<dependency>
<groupId>com.algorand</groupId>
<artifactId>algosdk</artifactId>
<version>1.9.0</version>
<version>1.10.0</version>
</dependency>
```

Expand Down
10 changes: 5 additions & 5 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<groupId>com.algorand</groupId>
<artifactId>algosdk</artifactId>
<version>1.9.0</version>
<version>1.10.0</version>
<packaging>jar</packaging>

<name>${project.groupId}:${project.artifactId}</name>
Expand Down Expand Up @@ -55,17 +55,17 @@
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.7.0</version>
<version>2.9.9</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.7.9</version>
<version>2.9.9</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.7.9</version>
<version>2.9.9.3</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
Expand Down Expand Up @@ -117,7 +117,7 @@
<dependency>
<groupId>org.msgpack</groupId>
<artifactId>jackson-dataformat-msgpack</artifactId>
<version>0.8.16</version>
<version>0.9.0</version>
</dependency>
<dependency>
<groupId>org.threeten</groupId>
Expand Down
113 changes: 98 additions & 15 deletions src/main/java/com/algorand/algosdk/account/Account.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ public class Account {
private static final String SIGN_ALGO = "EdDSA";
private static final int PK_SIZE = 32;
private static final int PK_X509_PREFIX_LENGTH = 12; // Ed25519 specific
private static final int SK_PKCS_PREFIX_LENGTH = 16; // Ed25519 specific
private static final int SK_SEPARATOR_LENGTH = 3; // separator is 0x81 0x21 0x00
private static final int SK_SIZE = 32;
private static final int SK_SIZE_BITS = SK_SIZE * 8;
private static final byte[] BID_SIGN_PREFIX = ("aB").getBytes(StandardCharsets.UTF_8);
Expand Down Expand Up @@ -73,6 +75,37 @@ private Account(SecureRandom randomSrc) throws NoSuchAlgorithmException {
this.address = new Address(Arrays.copyOf(raw, raw.length));
}

// Derive an Account object from only keypair: {privateKey, publicKey}
public Account(KeyPair pkPair) {
CryptoProvider.setupIfNeeded();
if (!pkPair.getPrivate().getAlgorithm().equals(KEY_ALGO)) {
throw new IllegalArgumentException("Keypair algorithm do not match with expected " + KEY_ALGO);
}
this.privateKeyPair = pkPair;
byte[] raw = this.getClearTextPublicKey();
this.address = new Address(Arrays.copyOf(raw, raw.length));
}

// Derive an Account from only private key
public Account(PrivateKey pk) throws NoSuchAlgorithmException {
CryptoProvider.setupIfNeeded();
if (!pk.getAlgorithm().equals(KEY_ALGO))
throw new IllegalArgumentException("Account cannot be generated from a non-Ed25519 key");

if (pk.getEncoded().length != SK_PKCS_PREFIX_LENGTH + SK_SIZE + SK_SEPARATOR_LENGTH + PK_SIZE)
throw new RuntimeException("Private Key cannot generate clear private key bytes");

byte[] clearPrivateKey = new byte[SK_SIZE];
System.arraycopy(pk.getEncoded(), SK_PKCS_PREFIX_LENGTH, clearPrivateKey, 0, SK_SIZE);

KeyPairGenerator gen = KeyPairGenerator.getInstance(KEY_ALGO);
gen.initialize(SK_SIZE_BITS, new FixedSecureRandom(clearPrivateKey));
this.privateKeyPair = gen.generateKeyPair();
// now, convert public key to an address
byte[] raw = this.getClearTextPublicKey();
this.address = new Address(Arrays.copyOf(raw, raw.length));
}

/**
* Convenience method for getting the underlying public key for raw operations.
* @return the public key as length 32 byte array.
Expand Down Expand Up @@ -255,7 +288,7 @@ public static BigInteger estimatedEncodedSize(Transaction tx) throws NoSuchAlgor
// SignTransaction does the signing, but also sets authAddr which is not desired here.
long length = Encoder.encodeToMsgPack(
new SignedTransaction(
tx,
tx,
new Account().rawSignBytes(
Arrays.copyOf(tx.bytesToSign(), tx.bytesToSign().length)),
tx.txID())).length;
Expand Down Expand Up @@ -307,10 +340,6 @@ public Signature signBytes(byte[] bytes) throws NoSuchAlgorithmException {
* @throws NoSuchAlgorithmException if could not sign tx
*/
public SignedTransaction signMultisigTransaction(MultisigAddress from, Transaction tx) throws NoSuchAlgorithmException {
// check that from addr of tx matches multisig preimage
if (!tx.sender.toString().equals(from.toString())) {
throw new IllegalArgumentException("Transaction sender does not match multisig account");
}
// check that account secret key is in multisig pk list
Ed25519PublicKey myPK = this.getEd25519PublicKey();
int myI = from.publicKeys.indexOf(myPK);
Expand All @@ -327,7 +356,13 @@ public SignedTransaction signMultisigTransaction(MultisigAddress from, Transacti
mSig.subsigs.add(new MultisigSubsig(from.publicKeys.get(i)));
}
}
return new SignedTransaction(tx, mSig, txSig.transactionID);
// generate signed transaction
SignedTransaction stx = new SignedTransaction(tx, mSig, txSig.transactionID);
// if the transaction sender address is not multi-sig address
// set the auth address as the multi-sig address
if (!tx.sender.equals(from.toAddress()))
stx.authAddr = from.toAddress();
return stx;
}

/**
Expand All @@ -340,13 +375,19 @@ public static SignedTransaction mergeMultisigTransactions(SignedTransaction... t
throw new IllegalArgumentException("cannot merge a single transaction");
}
SignedTransaction merged = txs[0];
for (int i = 0; i < txs.length; i++) {
for (SignedTransaction tx : txs) {
// check that multisig parameters match
SignedTransaction tx = txs[i];
if (tx.mSig.version != merged.mSig.version ||
tx.mSig.threshold != merged.mSig.threshold) {
throw new IllegalArgumentException("transaction msig parameters do not match");
}
// check multi-sig address match
if (!tx.mSig.convertToMultisigAddress().equals(merged.mSig.convertToMultisigAddress()))
throw new IllegalArgumentException("transaction msig addresses do not match");
// check that authAddr match
if (!tx.authAddr.equals(merged.authAddr)) {
throw new IllegalArgumentException("transaction msig auth addr do not match");
}
for (int j = 0; j < tx.mSig.subsigs.size(); j++) {
MultisigSubsig myMsig = merged.mSig.subsigs.get(j);
MultisigSubsig theirMsig = tx.mSig.subsigs.get(j);
Expand Down Expand Up @@ -514,6 +555,25 @@ public LogicsigSignature appendToLogicsig(LogicsigSignature lsig) throws Illegal

}

public static SignedTransaction signLogicTransactionWithAddress(LogicsigSignature lsig, Address lsigAddr, Transaction tx)
throws IllegalArgumentException, IOException {
try {
if (!lsig.verify(lsigAddr))
throw new IllegalArgumentException("verification failed on logic sig");
} catch (NoSuchAlgorithmException ex) {
throw new IllegalArgumentException("verification failed on logic sig", ex);
}

try {
SignedTransaction stx = new SignedTransaction(tx, lsig, tx.txID());
if (!stx.tx.sender.equals(lsigAddr))
stx.authAddr = lsigAddr;
return stx;
} catch (Exception ex) {
throw new IOException("could not encode transactions", ex);
}
}

/**
* Creates SignedTransaction from LogicsigSignature and Transaction.
* LogicsigSignature must be valid and verifiable against transaction sender field.
Expand All @@ -522,14 +582,20 @@ public LogicsigSignature appendToLogicsig(LogicsigSignature lsig) throws Illegal
* @return SignedTransaction
*/
public static SignedTransaction signLogicsigTransaction(LogicsigSignature lsig, Transaction tx) throws IllegalArgumentException, IOException {
if (!lsig.verify(tx.sender)) {
throw new IllegalArgumentException("verification failed");
}

boolean hasSig = lsig.sig != null;
boolean hasMsig = lsig.msig != null;
Address lsigAddr;
try {
return new SignedTransaction(tx, lsig, tx.txID());
if (hasSig) {
lsigAddr = tx.sender;
} else if (hasMsig) {
lsigAddr = lsig.msig.convertToMultisigAddress().toAddress();
} else {
lsigAddr = lsig.toAddress();
}
return Account.signLogicTransactionWithAddress(lsig, lsigAddr, tx);
} catch (Exception ex) {
throw new IOException("could not encode transactions", ex);
throw new IOException("could not sign transaction", ex);
}
}

Expand Down Expand Up @@ -569,8 +635,25 @@ public byte[] toSeed() throws GeneralSecurityException {
return Mnemonic.toKey(mnemonic);
}

@Override
public boolean equals(Object obj) {
if (!(obj instanceof Account))
return false;
Account oAccount = (Account) obj;
boolean addressMatch = Arrays.equals(this.address.getBytes(), oAccount.address.getBytes());
boolean privateKeyMatch = Arrays.equals(
this.privateKeyPair.getPrivate().getEncoded(),
oAccount.privateKeyPair.getPrivate().getEncoded()
);
boolean publicKeyMatch = Arrays.equals(
this.privateKeyPair.getPublic().getEncoded(),
oAccount.privateKeyPair.getPublic().getEncoded()
);
return addressMatch && privateKeyMatch && publicKeyMatch;
}

// Return a pre-set seed in response to nextBytes or generateSeed
private static class FixedSecureRandom extends SecureRandom {
public static class FixedSecureRandom extends SecureRandom {
private final byte[] fixedValue;
private int index = 0;

Expand Down
Loading

0 comments on commit cda8a83

Please sign in to comment.