Skip to content

Commit

Permalink
Merge pull request #333 from massalabs/feat/asesrt-is-smart-contract
Browse files Browse the repository at this point in the history
Add assertIsSmartContract function and tests
  • Loading branch information
Ben-Rey authored Mar 19, 2024
2 parents c8d9a42 + f173b8c commit 8ecb61c
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 15 deletions.
44 changes: 44 additions & 0 deletions assembly/std/__tests__/assertIsSmartContract.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {
Address,
addAddressToLedger,
assertIsSmartContract,
setBytecodeOf,
} from '../..';

const validSCAddress = 'AS1uku77MYEHy3i12WeERtD2JQzeKePz5zmJjWCq6A8wWyZakccQ';
const invalidSCAddress = 'AS1uu77MYeERtD2JQzeKePz5zmJjWCq6A';
const userAddress = 'AU1UpieCqHKxzYYh47YbQEHdecMQJM5VsFMn7pS2tn37bxsfdxJ5';
const noByteAddress = 'AS1uku77MYEHy3i12WeERtD2JQzeKePz5zmJjWCq6A8wWyZakccQ';

describe('assertAddressIsSmartContract', (): void => {
throws('for an invalid address', (): void => {
// The address is invalid but the mock ledger doesn't know that
// so we can still set the bytecode and we are sure it fail for the right reason
addAddressToLedger(invalidSCAddress);
setBytecodeOf(new Address(validSCAddress), [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]);

assertIsSmartContract(invalidSCAddress);
});

throws('for a user address', (): void => {
addAddressToLedger(userAddress);
assertIsSmartContract(userAddress);
});

throws('for a sc address with no bytecode', (): void => {
addAddressToLedger(noByteAddress);
assertIsSmartContract(noByteAddress);
});

throws('for a sc address with bytecode empty', (): void => {
addAddressToLedger(validSCAddress);
setBytecodeOf(new Address(validSCAddress), []);
assertIsSmartContract(validSCAddress);
});

it('should not throw for a valid smart contract address', (): void => {
addAddressToLedger(validSCAddress);
setBytecodeOf(new Address(validSCAddress), [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]);
assertIsSmartContract(validSCAddress);
});
});
23 changes: 23 additions & 0 deletions assembly/std/utils/address.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { env } from '../../env';
import { Address } from '../address';
import { getBytecodeOf } from '../contract';

/**
* Retrieves an 'Address' object from the given public key.
Expand Down Expand Up @@ -59,3 +60,25 @@ export function json2Address(str: string): Array<Address> {
const a = str.split(',');
return a.map<Address>((x) => new Address(x.substring(1, x.length - 1)));
}

/**
* Asserts that the given address is an already deployed non-destroyed contract.
*
* @param address - The address to check.
*
* @throws
* If the address is not a valid smart contract address.
* If no bytecode is found at the address.
*/
export function assertIsSmartContract(address: string): void {
// Check if it's a valid smart contract address
assert(validateAddress(address), `${address} is not a valid Address`);
assert(
address.startsWith('AS'),
`${address} is not a smart contract Address`,
);
// Throw if no contract has been deployed at this address
const bytecode = getBytecodeOf(new Address(address));
// Check if the contract has been destroyed
assert(bytecode.length > 0, `No bytecode found at address: ${address}`);
}
22 changes: 17 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 21 additions & 10 deletions vm-mock/vm.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ let mockedOriginOpId = '';
* @param {string} msg error message
*/
function ERROR(msg) {
console.log('\x1b[31m%s\x1b[0m','Vm-mock error: ' + msg);
console.log('\x1b[31m%s\x1b[0m', 'Vm-mock error: ' + msg);
throw new Error(msg);
}

Expand Down Expand Up @@ -188,7 +188,6 @@ export default function createMockedABI(
* @returns {Uint8Array} - The serialized keys.
*/
function serializeKeys(keysArr) {

const serialized = [];
const lengthBytes = numToLittleEndianBytes(keysArr.length);

Expand Down Expand Up @@ -258,6 +257,7 @@ export default function createMockedABI(
ledger.set(address, {
storage: new Map(),
contract: '',
balance: BigInt(0),
});
},

Expand Down Expand Up @@ -311,6 +311,7 @@ export default function createMockedABI(
ledger.set(a, {
storage: new Map(),
contract: '',
balance: BigInt(0),
});
}
const addressStorage = ledger.get(a).storage;
Expand Down Expand Up @@ -435,11 +436,13 @@ export default function createMockedABI(
// map the AssemblyScript file with the contractAddresses generated
assembly_script_create_sc(_) {
const newAddress = { _value: generateDumbAddress(), _isValid: true };
const newAddressLedger = {

ledger.set(newAddress._value, {
storage: new Map(),
contract: '',
};
ledger.set(newAddress._value, newAddressLedger);
balance: BigInt(0),
});

return newArrayBuffer(newAddress._value);
},

Expand Down Expand Up @@ -484,6 +487,7 @@ export default function createMockedABI(
ledger.set(callerAddress, {
storage: new Map(),
contract: '',
balance: BigInt(0),
});
}
// updating the callStack
Expand All @@ -503,6 +507,7 @@ export default function createMockedABI(
ledger.set(callerAddress, {
storage: new Map(),
contract: '',
balance: BigInt(0),
});
}
callStack = callerAddress + ' , ' + contractAddress;
Expand Down Expand Up @@ -687,17 +692,21 @@ export default function createMockedABI(
balance: BigInt(0),
});
}
if (ledger.get(toAddress).balance === undefined) {
ledger.get(toAddress).balance = BigInt(0);
}

// get last elt of callStack
const callStackArray = callStack.split(' , ');
const fromAddress = callStackArray[callStackArray.length - 1];

if (!ledger.has(fromAddress)) {
ERROR(`Sending address ${fromAddress} does not exist in ledger.`);
}
const senderBalance = ledger.get(fromAddress).balance;

if (senderBalance < BigInt(amount)) {
ERROR('not enough balance to transfer ' + amount + ' coins.');
const senderBalance = ledger.get(fromAddress).balance;
if (senderBalance == undefined || senderBalance < BigInt(amount)) {
ERROR('Not enough balance to transfer ' + amount + ' coins.');
}

ledger.get(toAddress).balance += BigInt(amount);
Expand Down Expand Up @@ -774,7 +783,9 @@ export default function createMockedABI(

const pubKeyBuf = getArrayBuffer(publicKeyPtr);
if (pubKeyBuf.byteLength !== 64) {
ERROR('Invalid public key length. Expected 64 bytes uncompressed secp256k1 public key');
ERROR(
'Invalid public key length. Expected 64 bytes uncompressed secp256k1 public key',
);
}

const digest = hashMessage(new Uint8Array(getArrayBuffer(dataPtr)));
Expand Down Expand Up @@ -815,7 +826,7 @@ export default function createMockedABI(
},

assembly_script_get_origin_operation_id() {
if(mockedOriginOpId !== '') {
if (mockedOriginOpId !== '') {
return newString(mockedOriginOpId);
}
return newString(generateRandOpId());
Expand Down

0 comments on commit 8ecb61c

Please sign in to comment.