Skip to content

Commit daaa870

Browse files
committed
feat: add iota_account2
1 parent f409c20 commit daaa870

File tree

3 files changed

+172
-4
lines changed

3 files changed

+172
-4
lines changed

examples/move/iota_account/sources/iota_account.move

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,8 @@ public fun borrow_field<Name: copy + drop + store, Value: store>(
9393
name: Name,
9494
ctx: &TxContext,
9595
): &Value {
96-
let id = &self.id;
9796
// Check that the sender of this TX is the account.
98-
assert!(id.uid_to_address() == ctx.sender());
97+
assert!(self.id.uid_to_address() == ctx.sender());
9998

10099
dynamic_field::borrow(&self.id, name)
101100
}
@@ -105,9 +104,8 @@ public fun borrow_field_mut<Name: copy + drop + store, Value: store>(
105104
name: Name,
106105
ctx: &TxContext,
107106
): &mut Value {
108-
let id = &self.id;
109107
// Check that the sender of this TX is the account.
110-
assert!(id.uid_to_address() == ctx.sender());
108+
assert!(self.id.uid_to_address() == ctx.sender());
111109

112110
dynamic_field::borrow_mut(&mut self.id, name)
113111
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
[package]
2+
name = "iota_account2"
3+
edition = "2024.beta" # edition = "legacy" to use legacy (pre-2024) Move
4+
# license = "" # e.g., "MIT", "GPL", "Apache 2.0"
5+
# authors = ["..."] # e.g., ["Joe Smith ([email protected])", "John Snow ([email protected])"]
6+
7+
[dependencies]
8+
Iota = { local = "../../../crates/iota-framework/packages/iota-framework" }
9+
10+
# For remote import, use the `{ git = "...", subdir = "...", rev = "..." }`.
11+
# Revision can be a branch, a tag, and a commit hash.
12+
# MyRemotePackage = { git = "https://some.remote/host.git", subdir = "remote/path", rev = "main" }
13+
14+
# For local dependencies use `local = path`. Path is relative to the package root
15+
# Local = { local = "../path/to" }
16+
17+
# To resolve a version conflict and force a specific version for dependency
18+
# override use `override = true`
19+
# Override = { local = "../conflicting/version", override = true }
20+
21+
[addresses]
22+
iota_account2 = "0x0"
23+
24+
# Named addresses will be accessible in Move as `@name`. They're also exported:
25+
# for example, `std = "0x1"` is exported by the Standard Library.
26+
# alice = "0xA11CE"
27+
28+
[dev-dependencies]
29+
# The dev-dependencies section allows overriding dependencies for `--test` and
30+
# `--dev` modes. You can introduce test-only dependencies here.
31+
# Local = { local = "../path/to/dev-build" }
32+
33+
[dev-addresses]
34+
# The dev-addresses section allows overwriting named addresses for the `--test`
35+
# and `--dev` modes.
36+
# alice = "0xB0B"
37+
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
module iota_account2::iota_account;
2+
3+
use iota::account::{Self, AuthenticatorInfoV1};
4+
use iota::auth_context::AuthContext;
5+
use iota::dynamic_field;
6+
use iota::ecdsa_k1;
7+
use iota::ecdsa_r1;
8+
use iota::ed25519;
9+
10+
/// A dynamic field name for the account owner public key.
11+
const IOTACCOUNT_OWNER_PUBKEY: vector<u8> = b"IOTACCOUNT_OWNER_PUBKEY";
12+
13+
/// This struct represents an IOTA account on-chain.
14+
/// It holds all the related data as dynamic fields to simplify updates and migrations.
15+
public struct IOTAccount has key {
16+
id: UID,
17+
}
18+
19+
// --------------------------------------- Creation ---------------------------------------
20+
21+
/// Creates a new `IOTAccount` as a shared object with the given authenticator.
22+
///
23+
/// `authenticator` is expect to have the following signature:
24+
///
25+
/// public fun authenticate(self: &IOTAccount, signature: vector<u8>, _: &AuthContext, _: &TxContext) { ... }
26+
///
27+
/// And it is expected to verify the `signature` against the public key stored in the account.
28+
///
29+
/// There are several ready-made authenticators available in this module:
30+
/// - `authenticate_ed25519`
31+
/// - `authenticate_secp256k1`
32+
/// - `authenticate_secp256r1`
33+
public fun create(pubkey: vector<u8>, authenticator: AuthenticatorInfoV1, ctx: &mut TxContext) {
34+
// Create an account object.
35+
let mut account = IOTAccount { id: object::new(ctx) };
36+
37+
let account_id = &mut account.id;
38+
39+
// Add the account owner public key as a dynamic field.
40+
dynamic_field::add(account_id, IOTACCOUNT_OWNER_PUBKEY, pubkey);
41+
42+
// Add the authenticator info as a dynamic field.
43+
dynamic_field::add(account_id, account::authenticator_df_name(), authenticator);
44+
45+
// Turn the account object into a mutable shared object.
46+
iota::transfer::share_object(account);
47+
}
48+
49+
// --------------------------------------- Field Operations ---------------------------------------
50+
51+
/// Adds a new dynamic field to the account.
52+
public fun add_field<Name: copy + drop + store, Value: store>(
53+
self: &mut IOTAccount,
54+
name: Name,
55+
value: Value,
56+
ctx: &TxContext,
57+
) {
58+
// Check that the sender of this TX is the account.
59+
assert!(self.id.uid_to_address() == ctx.sender());
60+
61+
// Add a new field.
62+
dynamic_field::add(&mut self.id, name, value);
63+
}
64+
65+
/// Removes a dynamic field from the account.
66+
public fun remove_field<Name: copy + drop + store, Value: store>(
67+
self: &mut IOTAccount,
68+
name: Name,
69+
ctx: &TxContext,
70+
): Value {
71+
// Check that the sender of this TX is the account.
72+
assert!(self.id.uid_to_address() == ctx.sender());
73+
74+
// Remove a new field.
75+
dynamic_field::remove(&mut self.id, name)
76+
}
77+
78+
public fun borrow_field<Name: copy + drop + store, Value: store>(
79+
self: &IOTAccount,
80+
name: Name,
81+
ctx: &TxContext,
82+
): &Value {
83+
// Check that the sender of this TX is the account.
84+
assert!(self.id.uid_to_address() == ctx.sender());
85+
86+
dynamic_field::borrow(&self.id, name)
87+
}
88+
89+
public fun borrow_field_mut<Name: copy + drop + store, Value: store>(
90+
self: &mut IOTAccount,
91+
name: Name,
92+
ctx: &TxContext,
93+
): &mut Value {
94+
// Check that the sender of this TX is the account.
95+
assert!(self.id.uid_to_address() == ctx.sender());
96+
97+
dynamic_field::borrow_mut(&mut self.id, name)
98+
}
99+
100+
// --------------------------------------- Authentication ---------------------------------------
101+
102+
/// Ed25519 signature authenticator.
103+
public fun authenticate_ed25519(
104+
self: &IOTAccount,
105+
signature: vector<u8>,
106+
_: &AuthContext,
107+
ctx: &TxContext,
108+
) {
109+
let pubkey: &vector<u8> = borrow_field(self, IOTACCOUNT_OWNER_PUBKEY, ctx);
110+
assert!(ed25519::ed25519_verify(&signature, pubkey, ctx.digest()));
111+
}
112+
113+
/// Secp256k1 signature authenticator.
114+
public fun authenticate_secp256k1(
115+
self: &IOTAccount,
116+
signature: vector<u8>,
117+
_: &AuthContext,
118+
ctx: &TxContext,
119+
) {
120+
let pubkey: &vector<u8> = borrow_field(self, IOTACCOUNT_OWNER_PUBKEY, ctx);
121+
assert!(ecdsa_k1::secp256k1_verify(&signature, pubkey, ctx.digest(), 0));
122+
}
123+
124+
/// Secp256r1 signature authenticator.
125+
public fun authenticate_secp256r1(
126+
self: &IOTAccount,
127+
signature: vector<u8>,
128+
_: &AuthContext,
129+
ctx: &TxContext,
130+
) {
131+
let pubkey: &vector<u8> = borrow_field(self, IOTACCOUNT_OWNER_PUBKEY, ctx);
132+
assert!(ecdsa_r1::secp256r1_verify(&signature, pubkey, ctx.digest(), 0));
133+
}

0 commit comments

Comments
 (0)