lang: Remove belongs_to in favor of has_one (#459)

This commit is contained in:
Kirill Fomichev 2021-07-03 21:58:23 +03:00 committed by GitHub
parent 6c44ca94d7
commit 2d974604ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 59 additions and 58 deletions

View File

@ -19,6 +19,7 @@ incremented for features.
### Breaking Changes
* cli: Remove `.spec` suffix on TypeScript tests files ([#441](https://github.com/project-serum/anchor/pull/441)).
* lang: Remove `belongs_to` constraint ([#459](https://github.com/project-serum/anchor/pull/459)).
## [0.10.0] - 2021-06-27

@ -1 +1 @@
Subproject commit a6c389d6ece753d83bff1cff38d315775fefb467
Subproject commit a72e59a9b263b7e083af737669f12f5e3ee1997c

View File

@ -23,7 +23,7 @@ mod errors {
Ok(())
}
pub fn belongs_to_error(_ctx: Context<BelongsToError>) -> Result<()> {
pub fn has_one_error(_ctx: Context<HasOneError>) -> Result<()> {
Ok(())
}
@ -42,9 +42,9 @@ pub struct MutError<'info> {
}
#[derive(Accounts)]
pub struct BelongsToError<'info> {
#[account(init, belongs_to = owner)]
my_account: ProgramAccount<'info, BelongsToAccount>,
pub struct HasOneError<'info> {
#[account(init, has_one = owner)]
my_account: ProgramAccount<'info, HasOneAccount>,
owner: AccountInfo<'info>,
rent: Sysvar<'info, Rent>,
}
@ -56,7 +56,7 @@ pub struct SignerError<'info> {
}
#[account]
pub struct BelongsToAccount {
pub struct HasOneAccount {
owner: Pubkey,
}

View File

@ -61,23 +61,23 @@ describe("errors", () => {
}
});
it("Emits a belongs to error", async () => {
it("Emits a has one error", async () => {
try {
const account = new Account();
const tx = await program.rpc.belongsToError({
const tx = await program.rpc.hasOneError({
accounts: {
myAccount: account.publicKey,
owner: anchor.web3.SYSVAR_RENT_PUBKEY,
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
},
instructions: [
await program.account.belongsToAccount.createInstruction(account),
await program.account.hasOneAccount.createInstruction(account),
],
signers: [account],
});
assert.ok(false);
} catch (err) {
const errMsg = "A belongs_to constraint was violated";
const errMsg = "A has_one constraint was violated";
assert.equal(err.toString(), errMsg);
assert.equal(err.msg, errMsg);
assert.equal(err.code, 141);

View File

@ -739,7 +739,7 @@ pub struct DepositLocked<'info> {
// Program specific.
registry: ProgramState<'info, Registry>,
registrar: ProgramAccount<'info, Registrar>,
#[account(belongs_to = registrar, has_one = beneficiary)]
#[account(has_one = registrar, has_one = beneficiary)]
member: ProgramAccount<'info, Member>,
#[account(signer)]
beneficiary: AccountInfo<'info>,
@ -755,7 +755,7 @@ pub struct Stake<'info> {
pool_mint: CpiAccount<'info, Mint>,
// Member.
#[account(mut, has_one = beneficiary, belongs_to = registrar)]
#[account(mut, has_one = beneficiary, has_one = registrar)]
member: ProgramAccount<'info, Member>,
#[account(signer)]
beneficiary: AccountInfo<'info>,
@ -794,7 +794,7 @@ pub struct StartUnstake<'info> {
// Member.
#[account(init)]
pending_withdrawal: ProgramAccount<'info, PendingWithdrawal>,
#[account(has_one = beneficiary, belongs_to = registrar)]
#[account(has_one = beneficiary, has_one = registrar)]
member: ProgramAccount<'info, Member>,
#[account(signer)]
beneficiary: AccountInfo<'info>,
@ -824,11 +824,11 @@ pub struct StartUnstake<'info> {
pub struct EndUnstake<'info> {
registrar: ProgramAccount<'info, Registrar>,
#[account(belongs_to = registrar, has_one = beneficiary)]
#[account(has_one = registrar, has_one = beneficiary)]
member: ProgramAccount<'info, Member>,
#[account(signer)]
beneficiary: AccountInfo<'info>,
#[account(mut, belongs_to = registrar, belongs_to = member, "!pending_withdrawal.burned")]
#[account(mut, has_one = registrar, has_one = member, "!pending_withdrawal.burned")]
pending_withdrawal: ProgramAccount<'info, PendingWithdrawal>,
// If we had ordered maps implementing Accounts we could do a constraint like
@ -859,7 +859,7 @@ pub struct Withdraw<'info> {
// Stake instance.
registrar: ProgramAccount<'info, Registrar>,
// Member.
#[account(belongs_to = registrar, has_one = beneficiary)]
#[account(has_one = registrar, has_one = beneficiary)]
member: ProgramAccount<'info, Member>,
#[account(signer)]
beneficiary: AccountInfo<'info>,
@ -912,7 +912,7 @@ pub struct WithdrawLocked<'info> {
// Program specific.
registry: ProgramState<'info, Registry>,
registrar: ProgramAccount<'info, Registrar>,
#[account(belongs_to = registrar, has_one = beneficiary)]
#[account(has_one = registrar, has_one = beneficiary)]
member: ProgramAccount<'info, Member>,
#[account(signer)]
beneficiary: AccountInfo<'info>,
@ -984,7 +984,7 @@ pub struct ClaimRewardCommon<'info> {
// Stake instance.
registrar: ProgramAccount<'info, Registrar>,
// Member.
#[account(mut, belongs_to = registrar, has_one = beneficiary)]
#[account(mut, has_one = registrar, has_one = beneficiary)]
member: ProgramAccount<'info, Member>,
#[account(signer)]
beneficiary: AccountInfo<'info>,
@ -993,7 +993,7 @@ pub struct ClaimRewardCommon<'info> {
#[account("BalanceSandbox::from(&balances_locked) == member.balances_locked")]
balances_locked: BalanceSandboxAccounts<'info>,
// Vendor.
#[account(belongs_to = registrar, has_one = vault)]
#[account(has_one = registrar, has_one = vault)]
vendor: ProgramAccount<'info, RewardVendor>,
#[account(mut)]
vault: AccountInfo<'info>,
@ -1016,7 +1016,7 @@ pub struct ExpireReward<'info> {
// Staking instance globals.
registrar: ProgramAccount<'info, Registrar>,
// Vendor.
#[account(mut, belongs_to = registrar, has_one = vault, has_one = expiry_receiver)]
#[account(mut, has_one = registrar, has_one = vault, has_one = expiry_receiver)]
vendor: ProgramAccount<'info, RewardVendor>,
#[account(mut)]
vault: CpiAccount<'info, TokenAccount>,

View File

@ -183,7 +183,7 @@ pub struct CreateTransaction<'info> {
#[derive(Accounts)]
pub struct Approve<'info> {
multisig: ProgramAccount<'info, Multisig>,
#[account(mut, belongs_to = multisig)]
#[account(mut, has_one = multisig)]
transaction: ProgramAccount<'info, Transaction>,
// One of the multisig owners. Checked in the handler.
#[account(signer)]
@ -209,7 +209,7 @@ pub struct ExecuteTransaction<'info> {
&[multisig.nonce],
])]
multisig_signer: AccountInfo<'info>,
#[account(mut, belongs_to = multisig)]
#[account(mut, has_one = multisig)]
transaction: ProgramAccount<'info, Transaction>,
}

View File

@ -41,8 +41,7 @@ use syn::parse_macro_input;
/// | `#[account(mut)]` | On `AccountInfo`, `ProgramAccount` or `CpiAccount` structs. | Marks the account as mutable and persists the state transition. |
/// | `#[account(init)]` | On `ProgramAccount` structs. | Marks the account as being initialized, skipping the account discriminator check. When using `init`, a `rent` `Sysvar` must be present in the `Accounts` struct. |
/// | `#[account(close = <target>)]` | On `ProgramAccount` and `Loader` structs. | Marks the account as being closed at the end of the instruction's execution, sending the rent exemption lamports to the specified <target>. |
/// | `#[account(belongs_to = <target>)]` | On `ProgramAccount` or `CpiAccount` structs | Checks the `target` field on the account matches the `target` field in the struct deriving `Accounts`. |
/// | `#[account(has_one = <target>)]` | On `ProgramAccount` or `CpiAccount` structs | Semantically different, but otherwise the same as `belongs_to`. |
/// | `#[account(has_one = <target>)]` | On `ProgramAccount` or `CpiAccount` structs | Checks the `target` field on the account matches the `target` field in the struct deriving `Accounts`. |
/// | `#[account(seeds = [<seeds>])]` | On `AccountInfo` structs | Seeds for the program derived address an `AccountInfo` struct represents. |
/// | `#[account(constraint = <expression>)]` | On any type deriving `Accounts` | Executes the given code as a constraint. The expression should evaluate to a boolean. |
/// | `#[account("<literal>")]` | Deprecated | Executes the given code literal as a constraint. The literal should evaluate to a boolean. |

View File

@ -22,8 +22,8 @@ pub enum ErrorCode {
// Constraints.
#[msg("A mut constraint was violated")]
ConstraintMut = 140,
#[msg("A belongs to constraint was violated")]
ConstraintBelongsTo,
#[msg("A has one constraint was violated")]
ConstraintHasOne,
#[msg("A signer constraint as violated")]
ConstraintSigner,
#[msg("A raw constraint was violated")]

View File

@ -1,6 +1,6 @@
use crate::{
CompositeField, Constraint, ConstraintAddress, ConstraintAssociatedGroup, ConstraintBelongsTo,
ConstraintClose, ConstraintExecutable, ConstraintGroup, ConstraintInit, ConstraintLiteral,
CompositeField, Constraint, ConstraintAddress, ConstraintAssociatedGroup, ConstraintClose,
ConstraintExecutable, ConstraintGroup, ConstraintHasOne, ConstraintInit, ConstraintLiteral,
ConstraintMut, ConstraintOwner, ConstraintRaw, ConstraintRentExempt, ConstraintSeedsGroup,
ConstraintSigner, ConstraintState, Field, PdaKind, Ty,
};
@ -43,7 +43,7 @@ pub fn linearize(c_group: &ConstraintGroup) -> Vec<Constraint> {
init,
mutable,
signer,
belongs_to,
has_one,
literal,
raw,
owner,
@ -73,7 +73,7 @@ pub fn linearize(c_group: &ConstraintGroup) -> Vec<Constraint> {
if let Some(c) = signer {
constraints.push(Constraint::Signer(c));
}
constraints.append(&mut belongs_to.into_iter().map(Constraint::BelongsTo).collect());
constraints.append(&mut has_one.into_iter().map(Constraint::HasOne).collect());
constraints.append(&mut literal.into_iter().map(Constraint::Literal).collect());
constraints.append(&mut raw.into_iter().map(Constraint::Raw).collect());
if let Some(c) = owner {
@ -101,7 +101,7 @@ fn generate_constraint(f: &Field, c: &Constraint) -> proc_macro2::TokenStream {
match c {
Constraint::Init(c) => generate_constraint_init(f, c),
Constraint::Mut(c) => generate_constraint_mut(f, c),
Constraint::BelongsTo(c) => generate_constraint_belongs_to(f, c),
Constraint::HasOne(c) => generate_constraint_has_one(f, c),
Constraint::Signer(c) => generate_constraint_signer(f, c),
Constraint::Literal(c) => generate_constraint_literal(c),
Constraint::Raw(c) => generate_constraint_raw(c),
@ -157,10 +157,7 @@ pub fn generate_constraint_mut(f: &Field, _c: &ConstraintMut) -> proc_macro2::To
}
}
pub fn generate_constraint_belongs_to(
f: &Field,
c: &ConstraintBelongsTo,
) -> proc_macro2::TokenStream {
pub fn generate_constraint_has_one(f: &Field, c: &ConstraintHasOne) -> proc_macro2::TokenStream {
let target = c.join_target.clone();
let ident = &f.ident;
let field = match &f.ty {
@ -169,7 +166,7 @@ pub fn generate_constraint_belongs_to(
};
quote! {
if &#field.#target != #target.to_account_info().key {
return Err(anchor_lang::__private::ErrorCode::ConstraintBelongsTo.into());
return Err(anchor_lang::__private::ErrorCode::ConstraintHasOne.into());
}
}
}

View File

@ -271,7 +271,7 @@ pub struct ConstraintGroup {
executable: Option<ConstraintExecutable>,
state: Option<ConstraintState>,
associated: Option<ConstraintAssociatedGroup>,
belongs_to: Vec<ConstraintBelongsTo>,
has_one: Vec<ConstraintHasOne>,
literal: Vec<ConstraintLiteral>,
raw: Vec<ConstraintRaw>,
close: Option<ConstraintClose>,
@ -305,7 +305,7 @@ pub enum Constraint {
Init(ConstraintInit),
Mut(ConstraintMut),
Signer(ConstraintSigner),
BelongsTo(ConstraintBelongsTo),
HasOne(ConstraintHasOne),
Literal(ConstraintLiteral),
Raw(ConstraintRaw),
Owner(ConstraintOwner),
@ -325,7 +325,7 @@ pub enum ConstraintToken {
Init(Context<ConstraintInit>),
Mut(Context<ConstraintMut>),
Signer(Context<ConstraintSigner>),
BelongsTo(Context<ConstraintBelongsTo>),
HasOne(Context<ConstraintHasOne>),
Literal(Context<ConstraintLiteral>),
Raw(Context<ConstraintRaw>),
Owner(Context<ConstraintOwner>),
@ -359,7 +359,7 @@ pub struct ConstraintMut {}
pub struct ConstraintSigner {}
#[derive(Debug, Clone)]
pub struct ConstraintBelongsTo {
pub struct ConstraintHasOne {
pub join_target: Expr,
}

View File

@ -1,7 +1,7 @@
use crate::{
ConstraintAddress, ConstraintAssociated, ConstraintAssociatedGroup, ConstraintAssociatedPayer,
ConstraintAssociatedSpace, ConstraintAssociatedWith, ConstraintBelongsTo, ConstraintClose,
ConstraintExecutable, ConstraintGroup, ConstraintInit, ConstraintLiteral, ConstraintMut,
ConstraintAssociatedSpace, ConstraintAssociatedWith, ConstraintClose, ConstraintExecutable,
ConstraintGroup, ConstraintHasOne, ConstraintInit, ConstraintLiteral, ConstraintMut,
ConstraintOwner, ConstraintRaw, ConstraintRentExempt, ConstraintSeeds, ConstraintSeedsGroup,
ConstraintSigner, ConstraintState, ConstraintToken, ConstraintTokenAuthority,
ConstraintTokenMint, Context, PdaKind, Ty,
@ -81,9 +81,16 @@ pub fn parse_token(stream: ParseStream) -> ParseResult<ConstraintToken> {
.join(stream.span())
.unwrap_or_else(|| ident.span());
match kw.as_str() {
"belongs_to" | "has_one" => ConstraintToken::BelongsTo(Context::new(
// Deprecated since 0.11
"belongs_to" => {
return Err(ParseError::new(
ident.span(),
"belongs_to is deprecated, please use has_one",
))
}
"has_one" => ConstraintToken::HasOne(Context::new(
span,
ConstraintBelongsTo {
ConstraintHasOne {
join_target: stream.parse()?,
},
)),
@ -190,7 +197,7 @@ pub struct ConstraintGroupBuilder<'ty> {
pub init: Option<Context<ConstraintInit>>,
pub mutable: Option<Context<ConstraintMut>>,
pub signer: Option<Context<ConstraintSigner>>,
pub belongs_to: Vec<Context<ConstraintBelongsTo>>,
pub has_one: Vec<Context<ConstraintHasOne>>,
pub literal: Vec<Context<ConstraintLiteral>>,
pub raw: Vec<Context<ConstraintRaw>>,
pub owner: Option<Context<ConstraintOwner>>,
@ -215,7 +222,7 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
init: None,
mutable: None,
signer: None,
belongs_to: Vec::new(),
has_one: Vec::new(),
literal: Vec::new(),
raw: Vec::new(),
owner: None,
@ -275,7 +282,7 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
init,
mutable,
signer,
belongs_to,
has_one,
literal,
raw,
owner,
@ -314,7 +321,7 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
init: into_inner!(init),
mutable: into_inner!(mutable),
signer: into_inner!(signer),
belongs_to: into_inner_vec!(belongs_to),
has_one: into_inner_vec!(has_one),
literal: into_inner_vec!(literal),
raw: into_inner_vec!(raw),
owner: into_inner!(owner),
@ -371,7 +378,7 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
ConstraintToken::Init(c) => self.add_init(c),
ConstraintToken::Mut(c) => self.add_mut(c),
ConstraintToken::Signer(c) => self.add_signer(c),
ConstraintToken::BelongsTo(c) => self.add_belongs_to(c),
ConstraintToken::HasOne(c) => self.add_has_one(c),
ConstraintToken::Literal(c) => self.add_literal(c),
ConstraintToken::Raw(c) => self.add_raw(c),
ConstraintToken::Owner(c) => self.add_owner(c),
@ -475,20 +482,17 @@ impl<'ty> ConstraintGroupBuilder<'ty> {
Ok(())
}
fn add_belongs_to(&mut self, c: Context<ConstraintBelongsTo>) -> ParseResult<()> {
fn add_has_one(&mut self, c: Context<ConstraintHasOne>) -> ParseResult<()> {
if self
.belongs_to
.has_one
.iter()
.filter(|item| item.join_target == c.join_target)
.count()
> 0
{
return Err(ParseError::new(
c.span(),
"belongs_to target already provided",
));
return Err(ParseError::new(c.span(), "has_one target already provided"));
}
self.belongs_to.push(c);
self.has_one.push(c);
Ok(())
}

View File

@ -58,7 +58,7 @@ const LangErrorCode = {
// Constraints.
ConstraintMut: 140,
ConstraintBelongsTo: 141,
ConstraintHasOne: 141,
ConstraintSigner: 142,
ConstraintRaw: 143,
ConstraintOwner: 144,
@ -119,7 +119,7 @@ const LangErrorMessage = new Map([
// Constraints.
[LangErrorCode.ConstraintMut, "A mut constraint was violated"],
[LangErrorCode.ConstraintBelongsTo, "A belongs_to constraint was violated"],
[LangErrorCode.ConstraintHasOne, "A has_one constraint was violated"],
[LangErrorCode.ConstraintSigner, "A signer constraint was violated"],
[LangErrorCode.ConstraintRaw, "A raw constraint as violated"],
[LangErrorCode.ConstraintOwner, "An owner constraint was violated"],