use of org.aion.precompiled.PrecompiledTransactionResult in project aion by aionnetwork.
the class TokenBridgeContractTest method testRingMap.
@Test
public void testRingMap() {
// override defaults
this.contract = new TokenBridgeContract(context(OWNER_ADDR, CONTRACT_ADDR), ExternalStateForTests.usingRepository(this.repository), OWNER_ADDR, CONTRACT_ADDR);
this.connector = this.contract.getConnector();
ListFVM encodingList = new ListFVM();
for (ECKey k : members) {
encodingList.add(new AddressFVM(k.getAddress()));
}
byte[] payload = new AbiEncoder(BridgeFuncSig.SIG_RING_INITIALIZE.getSignature(), encodingList).encodeBytes();
PrecompiledTransactionResult result = this.contract.execute(payload, DEFAULT_NRG);
assertTrue(result.getStatus().isSuccess());
// create input byte[]
byte[] callPayload = new byte[36];
byte[] encodeBytes = new AbiEncoder(BridgeFuncSig.PURE_RING_MAP.getSignature()).encodeBytes();
ECKey newKey = ECKeyFac.inst().create();
byte[] randomAddress = newKey.getAddress();
System.arraycopy(encodeBytes, 0, callPayload, 0, 4);
System.arraycopy(randomAddress, 0, callPayload, 4, 32);
// execute with valid input
PrecompiledTransactionResult transferResult = this.contract.execute(callPayload, DEFAULT_NRG);
assertTrue(transferResult.getStatus().isSuccess());
// execute with invalid input
PrecompiledTransactionResult transferResult2 = this.contract.execute(encodeBytes, DEFAULT_NRG);
assertEquals("FAILURE", transferResult2.getStatus().causeOfError);
}
use of org.aion.precompiled.PrecompiledTransactionResult in project aion by aionnetwork.
the class TokenBridgeContractTest method testRemoveRingMemberNotOwner.
@Test
public void testRemoveRingMemberNotOwner() {
// override defaults
this.contract = new TokenBridgeContract(context(OWNER_ADDR, CONTRACT_ADDR), ExternalStateForTests.usingRepository(this.repository), OWNER_ADDR, CONTRACT_ADDR);
this.connector = this.contract.getConnector();
ListFVM encodingList = new ListFVM();
for (ECKey k : members) {
encodingList.add(new AddressFVM(k.getAddress()));
}
// address null - fail
byte[] payload = new AbiEncoder(BridgeFuncSig.SIG_RING_REMOVE_MEMBER.getSignature()).encodeBytes();
PrecompiledTransactionResult result = this.contract.execute(payload, DEFAULT_NRG);
assertEquals("FAILURE", result.getStatus().causeOfError);
// add new member - fail
byte[] sig = new AbiEncoder(BridgeFuncSig.SIG_RING_REMOVE_MEMBER.getSignature(), encodingList).encodeBytes();
// the new member
byte[] newMember = ECKeyFac.inst().create().getAddress();
byte[] payload2 = new byte[4 + 32];
System.arraycopy(sig, 0, payload2, 0, 4);
System.arraycopy(newMember, 0, payload2, 4, 32);
PrecompiledTransactionResult result2 = this.contract.execute(payload2, DEFAULT_NRG);
assertEquals("FAILURE", result2.getStatus().causeOfError);
// initialize ring
byte[] ring = new AbiEncoder(BridgeFuncSig.SIG_RING_INITIALIZE.getSignature(), encodingList).encodeBytes();
this.contract.execute(ring, DEFAULT_NRG);
// remove member - fail, member does not exist
byte[] sig3 = new AbiEncoder(BridgeFuncSig.SIG_RING_REMOVE_MEMBER.getSignature(), encodingList).encodeBytes();
// the new member
byte[] newMember3 = ECKeyFac.inst().create().getAddress();
byte[] payload3 = new byte[4 + 32];
System.arraycopy(sig3, 0, payload3, 0, 4);
System.arraycopy(newMember3, 0, payload3, 4, 32);
PrecompiledTransactionResult result3 = this.contract.execute(payload3, DEFAULT_NRG);
assertEquals("FAILURE", result3.getStatus().causeOfError);
// override defaults
this.contract = new TokenBridgeContract(context(AddressUtils.ZERO_ADDRESS, CONTRACT_ADDR), ExternalStateForTests.usingRepository(this.repository), OWNER_ADDR, CONTRACT_ADDR);
this.connector = this.contract.getConnector();
// failure, member exists but sender is no longer owner
byte[] sig4 = new AbiEncoder(BridgeFuncSig.SIG_RING_REMOVE_MEMBER.getSignature(), encodingList).encodeBytes();
byte[] payload4 = new byte[4 + 32];
System.arraycopy(sig4, 0, payload4, 0, 4);
System.arraycopy(members[0].getAddress(), 0, payload4, 4, 32);
PrecompiledTransactionResult result4 = this.contract.execute(payload4, DEFAULT_NRG);
assertEquals("FAILURE", result4.getStatus().causeOfError);
}
use of org.aion.precompiled.PrecompiledTransactionResult in project aion by aionnetwork.
the class TokenBridgeContractTest method testTransferHugeListLength.
@Test
public void testTransferHugeListLength() {
BridgeTransfer[] transfers = new BridgeTransfer[10];
for (int i = 0; i < 10; i++) {
// generate a unique sourceTransactionHash for each transfer
byte[] sourceTransactionHash = HashUtil.h256(Integer.toString(i).getBytes());
transfers[i] = BridgeTransfer.getInstance(BigInteger.ONE, AddressSpecs.computeA0Address(HashUtil.h256(Integer.toHexString(i).getBytes())), sourceTransactionHash);
}
// setup
ReturnDataFromSetup fromSetup = setupForTest(transfers, members);
PrecompiledTransactionContext submitBundleContext = fromSetup.submitBundleContext;
byte[] payloadHash = fromSetup.payloadHash;
byte[] callPayload = fromSetup.callPayload;
callPayload[50] = (byte) 0x128;
PrecompiledTransactionResult transferResult = this.contract.execute(callPayload, DEFAULT_NRG);
assertEquals("FAILURE", transferResult.getStatus().causeOfError);
// VERIFICATION failure
assertThat(this.contract.execute(ByteUtil.merge(BridgeFuncSig.PURE_ACTION_MAP.getBytes(), payloadHash), 21000L).getReturnData()).isEqualTo(new byte[32]);
assertEquals("FAILURE", transferResult.getStatus().causeOfError);
// check that nothing has been changed from the failed transfer
for (BridgeTransfer b : transfers) {
assertThat(this.repository.getBalance(new AionAddress(b.getRecipient()))).isEqualTo(BigInteger.ZERO);
}
assertThat(this.repository.getBalance(CONTRACT_ADDR)).isEqualTo(BigInteger.valueOf(10));
assertThat(submitBundleContext.getInternalTransactions()).isEmpty();
assertThat(submitBundleContext.getLogs()).isEmpty();
}
use of org.aion.precompiled.PrecompiledTransactionResult in project aion by aionnetwork.
the class BridgeController method processBundles.
/**
* Assume bundleHash is not from external source, but rather calculated on our side (on the I/O
* layer), when {@link BridgeTransfer} list was being created.
*
* @param caller, address of the calling account. Used to check whether the address calling is
* the relay or not.
* @param sourceBlockHash, hash of a block on the source blockchain, each block may contain 1 to
* N bundles. Used as part of the bundleHash to tie a bundle to a block.
* @param transfers, {@link BridgeTransfer}
* @param signatures, a list of signatures from signatories that have signed the bundles.
* @return {@code ErrCode} indicating whether operation was successful
* @implNote assume the inputs are properly formatted
* @implNote will check whether any bundles are {@code 0} value transfers. In such a case, it
* indicates that the bridge has faulted, so we should immediately fail all transfers.
* @implNote {@link BridgeDeserializer} implicitly places a max size for each list to 512.
*/
public ProcessedResults processBundles(@Nonnull final byte[] caller, @Nonnull final byte[] transactionHash, @Nonnull final byte[] sourceBlockHash, @Nonnull final BridgeTransfer[] transfers, @Nonnull final byte[][] signatures) {
if (!isRingLocked())
return processError(ErrCode.RING_NOT_LOCKED);
if (!isRelayer(caller))
return processError(ErrCode.NOT_RELAYER);
if (!isWithinSignatureBounds(signatures.length))
return processError(ErrCode.INVALID_SIGNATURE_BOUNDS);
/*
* Computes a unique identifier of the transfer hash for each sourceBlockHash,
* uniqueness relies on the fact that each
*/
// verify bundleHash
byte[] hash = computeBundleHash(sourceBlockHash, transfers);
// previously successfully broadcast in.
if (bundleProcessed(hash)) {
// ATB 6-1, fixed bug: emit stored transactionHash instead of input transaction Hash
emitSuccessfulTransactionHash(this.connector.getBundle(hash));
return processSuccess(Collections.emptyList());
}
int signed = 0;
IExternalCapabilitiesForPrecompiled capabilities = CapabilitiesProvider.getExternalCapabilities();
for (byte[] sigBytes : signatures) {
if (capabilities.verifyISig(hash, sigBytes) && this.connector.getActiveMember(capabilities.getISigAddress(sigBytes))) {
signed++;
}
}
int minThresh = this.connector.getMinThresh();
if (signed < minThresh)
return processError(ErrCode.NOT_ENOUGH_SIGNATURES);
// otherwise, we're clear to proceed with transfers
List<PrecompiledTransactionResult> results = new ArrayList<>();
for (BridgeTransfer b : transfers) {
if (b.getTransferValue().compareTo(BigInteger.ZERO) == 0)
return processError(ErrCode.INVALID_TRANSFER);
/*
* Tricky here, we distinguish between two types of failures here:
*
* 1) A balance failure indicates we've failed to load the bridge with
* enough currency to execute, this means the whole transaction should
* fail and cause the bridge to exit
*
* 2) Any other failure indicates that either the contract had code,
* which means the contract is now considered null.
*
* For how this is documented, check the {@code Transferable}
* interface documentation.
*/
PrecompiledTransactionResult result;
if ((result = transferable.transfer(b.getRecipient(), b.getTransferValue())).getStatus().isFailed())
// no need to return list of transactions, since they're all being dropped
return processError(ErrCode.INVALID_TRANSFER);
// otherwise if transfer was successful
if (result.getStatus().isSuccess())
if (!emitDistributed(b.getSourceTransactionHash(), b.getRecipient(), b.getTransferValue()))
return processError(ErrCode.INVALID_TRANSFER);
results.add(result);
}
this.connector.setBundle(hash, transactionHash);
emitProcessedBundle(sourceBlockHash, hash);
return processSuccess(results);
}
use of org.aion.precompiled.PrecompiledTransactionResult in project aion by aionnetwork.
the class TokenBridgeContract method transfer.
/**
* Performs a transfer of value from one account to another, using a method that mimics to the
* best of it's ability the {@code CALL} opcode. There are some assumptions that become
* important for any caller to know:
*
* @implNote this method will check that the recipient account has no code. This means that we
* <b>cannot</b> do a transfer to any contract account.
* @implNote assumes that the {@code fromValue} derived from the worldState will never be null.
* @param dest recipient address
* @param value to be sent (in base units)
* @return {@code true} if value was performed, {@code false} otherwise
*/
public PrecompiledTransactionResult transfer(@Nonnull final byte[] dest, @Nonnull final BigInteger value) {
// some initial checks, treat as failure
if (this.externalState.getBalance(this.contractAddress).compareTo(value) < 0)
return new PrecompiledTransactionResult(TransactionStatus.nonRevertedFailure("FAILURE"), 0);
// assemble an internal transaction
AionAddress sender = this.contractAddress;
AionAddress destination = new AionAddress(dest);
BigInteger nonce = this.externalState.getNonce(sender);
byte[] dataToSend = new byte[0];
InternalTransaction tx = InternalTransaction.contractCallTransaction(RejectedStatus.NOT_REJECTED, sender, destination, nonce, value, dataToSend, 0L, 1L);
// add transaction to result
this.context.addInternalTransaction(tx);
// increase the nonce and do the transfer without executing code
this.externalState.incrementNonce(sender);
this.externalState.addBalance(sender, value.negate());
this.externalState.addBalance(destination, value);
// construct result
return new PrecompiledTransactionResult(TransactionStatus.successful(), 0);
}
Aggregations