Closed9

【Sui】1つトランザクションで関数を1度だけしか呼ばないように制限する方法

onigirionigiri

Sam (Sui Moveの開発者)さんが、最近話題のSPAMのコントラクトに面白い関数の仕掛けがあるとXで投稿していたので、それについて書いていきます。

その前に。Don't Trust. Verify

これはクリプトに投資する上でもっとも大切な言葉だと思っています。
世にあるmemeトークンやNFT、スマートコントラクトを読まずに大事なお金を使うのはとてもリスクが高いため、できるだけコントラクトを読むようにしています。わからない点は自分でコードを書いて検証していきたいです。

SPAM の increment_user_counter

Samさん曰く、 increment_user_counter はPTBで不正に(?)何回も呼べないようにうまく制限しているらしいです。確かに1回のトランザクションで何回もこの関数呼べてしまったら、spamの意味ないですからね😇

spamのコードには面白い仕掛けがある:https://github.com/juzybits/polymedia-spam/blob/main/src/sui/sources/spam.move#L88... increment_user_counterは、1つのPTBにつき最大1回の呼び出しを保証するために、意図的に*反*構成的である。 通常、このような関数は&mut UserCounterを受け取り、直接インクリメントします。これにより、同じPTB内でincrement_user_counter を複数回呼び出したり、ループ内でこのコードを呼び出す Move を書くことができます。しかし、このコードはUserCounterの所有権を取得し、それをインクリメントしてから送信者に戻すことで、それを巧みに防いでいる。なかなかいい感じだ。https://move-book.com/programmability/index.html! 関連する考え: increment_user_counter は1回/PTBだけ呼び出すことができるが、同じPTBは他の関数を自由に呼び出すことができる。increment_user_counterへの呼び出しを、いずれにせよ送信しようとしていたPTBに付加することによって)"パッシブ・スパミング "が流行することは間違いなく想像できる。

onigirionigiri

検証するため、以下のコードを書いてデプロイした

単純なカウンターのコントラクト

module onigiri::counter_test {
  public struct Counter has key {
    id: UID,
    value: u64,
  }
  fun init(ctx: &mut TxContext) {
    transfer::transfer(Counter {
      id: object::new(ctx),
      value: 0,
    }, ctx.sender())
  }
  public entry fun increment_normal(counter: &mut Counter) {
    counter.value = counter.value + 1;
  }
  public entry fun increment_once(mut counter: Counter, ctx: &TxContext) {
    counter.value = counter.value + 1;
    transfer::transfer(counter, ctx.sender());
  }
}
onigirionigiri

increment_normal はPTBで何回も呼ぶことができた

(これがspamでできたら最高だった😂)

sui client ptb \
--assign counter @0x6888c38926f171d8a3e40d5bf8c3ec591e9dd3b844c0cb5d8f36fea4319e1b13 \
--move-call 0x1fff040c25ceb22513e93339f39bacacf66a1d35dcfe86bc28f2517306bb8694::counter_test::increment_normal counter \
--move-call 0x1fff040c25ceb22513e93339f39bacacf66a1d35dcfe86bc28f2517306bb8694::counter_test::increment_normal counter \
--move-call 0x1fff040c25ceb22513e93339f39bacacf66a1d35dcfe86bc28f2517306bb8694::counter_test::increment_normal counter \
--gas-budget 50000000

実際のトランザクション
https://testnet.suivision.xyz/txblock/4o4b3Y3MzsZYKSFnzkAZsyhbsczvvJT7MRYgXCbVBW9i

onigirionigiri

一方、 increment_once はPTBで複数回呼ぼうとすると、エラーが発生した

エラー内容
PTB execution failed due to CommandArgumentError { arg_idx: 0, kind: InvalidValueUsage } in command 1. Transaction digest is: GGuoevC7qyTVKnqx1t4aknZmLjYhzvVkm5saH6J6KryB

sui client ptb \
--assign counter @0x6888c38926f171d8a3e40d5bf8c3ec591e9dd3b844c0cb5d8f36fea4319e1b13 \
--move-call 0x1fff040c25ceb22513e93339f39bacacf66a1d35dcfe86bc28f2517306bb8694::counter_test::increment_once counter \
--move-call 0x1fff040c25ceb22513e93339f39bacacf66a1d35dcfe86bc28f2517306bb8694::counter_test::increment_once counter \
--move-call 0x1fff040c25ceb22513e93339f39bacacf66a1d35dcfe86bc28f2517306bb8694::counter_test::increment_once counter \
--gas-budget 50000000

[warn] Client/Server api version mismatch, client api version : 1.24.0, server api version : 1.24.1
PTB execution failed due to CommandArgumentError { arg_idx: 0, kind: InvalidValueUsage } in command 1. Transaction digest is: GGuoevC7qyTVKnqx1t4aknZmLjYhzvVkm5saH6J6KryB

実際のトランザクション
https://testnet.suivision.xyz/txblock/GGuoevC7qyTVKnqx1t4aknZmLjYhzvVkm5saH6J6KryB

onigirionigiri

両関数の違い

increment_normalcounter: &mut Counter を受け取り、値を増やしているのに対し、
increment_oncemut counter: Counter を受け取り、値を増やした上で、オブジェクトを送信者に送信している

疑問点

まだMoveを学習し始めたばかりなのでよくわからない点が2つ。

  1. 関数定義で mut counter: Counter という引数の定義の仕方は初見だったため少し驚いた。いまだにこの部分の意味がよくわかっていないのは、Move, Rustの基礎知識が抜けているから。

  2. オブジェクトを送信者に戻すことでPTBで複数回呼ばれることを防いでいるようだが、なぜこれで防げるのかがわかっていない

onigirionigiri

お布施大歓迎です💧
$SUI でも $SPAM でも PRIME MACHIN でも😍

0x26d6693f212015b60492c165f45d46429baeb5cd366f85a2419d2eeab0b76518

onigirionigiri

疑問点1について

引数の mut counter: Counter は、 Move 2024 から導入されたものだった。

mutキーワードはタプルのデストラクチャリングや関数の引数で、ミュータブル変数を宣言するために使われます。

mut キーワードをつけないと immutable になり、変数への変更ができなくなる。

だが、
counter: &mut Countermut counter: Counter の違いはなんだろうか?

このスクラップは2024/05/30にクローズされました