-
Notifications
You must be signed in to change notification settings - Fork 220
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support different Soroban storage types #1664
Changes from 12 commits
92ab9df
cf0498d
14bc671
c559b00
c12608c
dfa8aa3
46367ed
ae8b540
9105d37
9a981e0
e5dd60a
2ef39f8
73928be
4e28700
1d9060d
9b41136
27c61ad
2826851
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,3 @@ | ||
*.js | ||
*.so | ||
*.key | ||
*.json | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import * as StellarSdk from '@stellar/stellar-sdk'; | ||
import { readFileSync } from 'fs'; | ||
import { expect } from 'chai'; | ||
import path from 'path'; | ||
import { fileURLToPath } from 'url'; | ||
import { call_contract_function } from './test_helpers.js'; | ||
|
||
const __filename = fileURLToPath(import.meta.url); | ||
const dirname = path.dirname(__filename); | ||
|
||
describe('Runtime Error', () => { | ||
let keypair; | ||
const server = new StellarSdk.SorobanRpc.Server( | ||
"https://soroban-testnet.stellar.org:443", | ||
); | ||
|
||
let contractAddr; | ||
let contract; | ||
before(async () => { | ||
|
||
console.log('Setting up runtime_error.sol contract tests...'); | ||
|
||
// read secret from file | ||
const secret = readFileSync('alice.txt', 'utf8').trim(); | ||
keypair = StellarSdk.Keypair.fromSecret(secret); | ||
|
||
let contractIdFile = path.join(dirname, '.soroban', 'contract-ids', 'Error.txt'); | ||
// read contract address from file | ||
contractAddr = readFileSync(contractIdFile, 'utf8').trim().toString(); | ||
|
||
// load contract | ||
contract = new StellarSdk.Contract(contractAddr); | ||
|
||
// initialize the contract | ||
await call_contract_function("init", server, keypair, contract); | ||
|
||
// call decrement once. The second call however will result in a runtime error | ||
await call_contract_function("decrement", server, keypair, contract); | ||
}); | ||
|
||
it('get correct initial counter', async () => { | ||
|
||
// decrement the counter again, resulting in a runtime error | ||
let res = await call_contract_function("decrement", server, keypair, contract); | ||
|
||
expect(res).to.contain('runtime_error: math overflow in runtime_error.sol:6:9-19'); | ||
}); | ||
|
||
}); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
contract storage_types { | ||
|
||
uint64 public temporary sesa = 1; | ||
uint64 public instance sesa1 = 1; | ||
uint64 public persistent sesa2 = 2; | ||
uint64 public sesa3 = 2; | ||
|
||
function inc() public { | ||
sesa++; | ||
sesa1++; | ||
sesa2++; | ||
sesa3++; | ||
} | ||
|
||
function dec() public { | ||
sesa--; | ||
sesa1--; | ||
sesa2--; | ||
sesa3--; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import * as StellarSdk from '@stellar/stellar-sdk'; | ||
import { readFileSync } from 'fs'; | ||
import { expect } from 'chai'; | ||
import path from 'path'; | ||
import { fileURLToPath } from 'url'; | ||
import { call_contract_function } from './test_helpers.js'; // Helper to interact with the contract | ||
|
||
const __filename = fileURLToPath(import.meta.url); | ||
const dirname = path.dirname(__filename); | ||
|
||
describe('StorageTypes', () => { | ||
let keypair; | ||
const server = new StellarSdk.SorobanRpc.Server( | ||
"https://soroban-testnet.stellar.org:443", | ||
); | ||
|
||
let contractAddr; | ||
let contract; | ||
before(async () => { | ||
console.log('Setting up storage_types contract tests...'); | ||
|
||
// Read secret from file | ||
const secret = readFileSync('alice.txt', 'utf8').trim(); | ||
keypair = StellarSdk.Keypair.fromSecret(secret); | ||
|
||
let contractIdFile = path.join(dirname, '.soroban', 'contract-ids', 'storage_types.txt'); | ||
// Read contract address from file | ||
contractAddr = readFileSync(contractIdFile, 'utf8').trim().toString(); | ||
|
||
// Load contract | ||
contract = new StellarSdk.Contract(contractAddr); | ||
|
||
// Initialize the contract if necessary (adapt according to your contract logic) | ||
await call_contract_function("init", server, keypair, contract); | ||
}); | ||
|
||
it('check initial values', async () => { | ||
// Check initial values of all storage variables | ||
let sesa = await call_contract_function("sesa", server, keypair, contract); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To expand testing coverage, consider adding a test case for scenarios where a variable lacks a specified storage type and defaults to Also, what I said previously about |
||
expect(sesa.toString()).eq("1"); | ||
|
||
let sesa1 = await call_contract_function("sesa1", server, keypair, contract); | ||
expect(sesa1.toString()).eq("1"); | ||
|
||
let sesa2 = await call_contract_function("sesa2", server, keypair, contract); | ||
expect(sesa2.toString()).eq("2"); | ||
|
||
let sesa3 = await call_contract_function("sesa3", server, keypair, contract); | ||
expect(sesa3.toString()).eq("2"); | ||
}); | ||
|
||
it('increment values', async () => { | ||
// Increment all values by calling the inc function | ||
await call_contract_function("inc", server, keypair, contract); | ||
|
||
// Check the incremented values | ||
let sesa = await call_contract_function("sesa", server, keypair, contract); | ||
expect(sesa.toString()).eq("2"); | ||
|
||
let sesa1 = await call_contract_function("sesa1", server, keypair, contract); | ||
expect(sesa1.toString()).eq("2"); | ||
|
||
let sesa2 = await call_contract_function("sesa2", server, keypair, contract); | ||
expect(sesa2.toString()).eq("3"); | ||
|
||
let sesa3 = await call_contract_function("sesa3", server, keypair, contract); | ||
expect(sesa3.toString()).eq("3"); | ||
}); | ||
|
||
it('decrement values', async () => { | ||
// Decrement all values by calling the dec function | ||
await call_contract_function("dec", server, keypair, contract); | ||
|
||
// Check the decremented values | ||
let sesa = await call_contract_function("sesa", server, keypair, contract); | ||
expect(sesa.toString()).eq("1"); | ||
|
||
let sesa1 = await call_contract_function("sesa1", server, keypair, contract); | ||
expect(sesa1.toString()).eq("1"); | ||
|
||
let sesa2 = await call_contract_function("sesa2", server, keypair, contract); | ||
expect(sesa2.toString()).eq("2"); | ||
|
||
let sesa3 = await call_contract_function("sesa3", server, keypair, contract); | ||
expect(sesa3.toString()).eq("2"); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -179,6 +179,11 @@ pub enum Token<'input> { | |
Default, | ||
YulArrow, | ||
|
||
// Storage types for Soroban | ||
Persistent, | ||
Temporary, | ||
Instance, | ||
|
||
Annotation(&'input str), | ||
} | ||
|
||
|
@@ -316,6 +321,9 @@ impl<'input> fmt::Display for Token<'input> { | |
Token::Default => write!(f, "default"), | ||
Token::YulArrow => write!(f, "->"), | ||
Token::Annotation(name) => write!(f, "@{name}"), | ||
Token::Persistent => write!(f, "persistent"), | ||
Token::Temporary => write!(f, "temporary"), | ||
Token::Instance => write!(f, "instance"), | ||
} | ||
} | ||
} | ||
|
@@ -553,6 +561,9 @@ static KEYWORDS: phf::Map<&'static str, Token> = phf_map! { | |
"unchecked" => Token::Unchecked, | ||
"assembly" => Token::Assembly, | ||
"let" => Token::Let, | ||
"persistent" => Token::Persistent, | ||
"temporary" => Token::Temporary, | ||
"instance" => Token::Instance, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This does affect Polkadot/Solana, but never mind, I don't know what other solution there is (other than a better parser generator). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can they be made context-sensitive so that they are only treated as keywords when used in the appropriate context, and in other contexts they would be allowed to be used. I'm not sure if that can be done here, but basically have it only if the target is soroban? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unfortunately the keywords can't be made context-sensitive with the current parser generator. Of course, this is something I would really love and this would be such a nicer feature (for lalrpop). Having said that, it might be possible to replace the keywords There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A way around this is to make these keywords Soroban specific, so they do not apply for Solana/Polkadot. This could be implemented in the lexer. |
||
}; | ||
|
||
impl<'input> Lexer<'input> { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -955,6 +955,24 @@ pub enum VariableAttribute { | |
|
||
/// `ovveride(<1>,*)` | ||
Override(Loc, Vec<IdentifierPath>), | ||
|
||
/// Storage type. | ||
StorageType(StorageType), | ||
} | ||
|
||
/// Soroban storage types. | ||
#[derive(Debug, PartialEq, Eq, Clone)] | ||
#[cfg_attr(feature = "pt-serde", derive(Serialize, Deserialize))] | ||
#[repr(u8)] // for cmp; order of variants is important | ||
pub enum StorageType { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This doesn't match the storagetype enum order on soroban side |
||
/// `Temporary` | ||
Temporary(Option<Loc>), | ||
|
||
/// `persistent` | ||
Persistent(Option<Loc>), | ||
|
||
/// `Instance` | ||
Instance(Option<Loc>), | ||
} | ||
|
||
/// A variable definition. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there any reason the order of the matching should match the order of the enum?