본문으로 건너뛰기

트랜잭션 서명 및 전송

Sui의 transaction은 입력을 바탕으로 실행되어 transaction 결과를 정의하는 특정 기능에 대한 호출을 나타낸다.

입력은 owned object, immutable object, shared object를 가리키는 object reference이거나, 예를 들어 Move call의 인자로 쓰이는 byte vector 같은 encoded value일 수 있다. transaction은 보통 PTB 구성하기 가이드에서 설명하는 방식대로 구성되며, 구성된 뒤 사용자가 서명하고 온체인에서 실행되도록 제출한다.

signature는 wallet이 소유한 private key로 제공되며, 그 public key는 transaction sender의 Sui address와 일치해야 한다.

Sui는 intent message의 Blake2b hash digest(intent || bcs bytes of tx_data)에 commit하는 signature를 생성하기 위해 SuiKeyPair를 사용한다. 현재 지원하는 signature scheme은 다음과 같다:

  • Ed25519 Pure

  • ECDSA Secp256k1

  • ECDSA Secp256r1

  • Multisig

  • zkLogin

Ed25519 Pure, ECDSA Secp256k1, ECDSA Secp256r1은 SuiKeyPair를 사용해 인스턴스화할 수 있으며 transaction 서명에 사용할 수 있다. 이 가이드는 Multisig와 zkLogin에는 적용되지 않으므로, 지침은 각 전용 페이지(다중 서명zkLogin 통합)를 참조한다.

signature와 transaction bytes가 있으면 transaction을 제출해 실행시킬 수 있다.

Workflow

다음 상위 수준 과정은 온체인 transaction을 구성하고 서명하고 실행하는 전체 흐름을 설명한다:

  • 여러 command를 연결한 Transaction을 만들어 transaction 데이터를 구성한다. 자세한 내용은 PTB 구성하기를 참조한다.

  • SDK의 내장 gas 추정과 coin selection이 gas coin을 선택한다.

  • 서명을 생성하도록 transaction에 서명한다.

  • Transaction과 그 signature를 제출해 온체인 실행을 요청한다.

정보

특정 gas coin을 사용하고 싶다면 먼저 gas 지불에 사용할 gas coin object ID를 찾고 이를 PTB에 명시적으로 사용한다. gas coin object가 없다면 splitCoin transaction을 사용해 gas coin object를 만든다. split coin transaction은 PTB의 첫 번째 transaction call이어야 한다.

Process multiple transactions from the same address

Sui SDK는 같은 address에서 여러 transaction을 처리하는 데 도움이 되는 transaction executor를 제공한다.

SerialTransactionExecutor

transaction을 하나씩 순차적으로 처리할 때는 SerialTransactionExecutor를 사용한다. 이 executor는 sender의 모든 coin을 가져와 하나의 coin으로 합친 뒤 모든 transaction에 사용한다.

SerialTransactionExecutor를 사용하면 PTB 전반의 object 입력 versioning을 처리하여 SequenceNumber error를 방지할 수 있다.

ParallelTransactionExecutor

같은 sender의 transaction을 동시에 처리하고 싶다면 ParallelTransactionExecutor를 사용한다. 이 클래스는 병렬 transaction이 해당 coin에 대해 equivocate하지 않도록 관리하는 gas coin pool을 만든다. 이 클래스는 transaction 전반에서 사용된 object를 추적하고, object 입력도 equivocate되지 않도록 그 처리 순서를 정렬한다.

Examples

다음 예시는 Rust, TypeScript, Sui CLI를 사용해 transaction에 서명하고 실행하는 방법을 보여준다.

Sui TypeScript SDK를 사용하면 key pair를 인스턴스화하고 그 public key와 Sui address를 파생하는 여러 방법이 있다.

import { fromHex } from '@mysten/bcs';
import { type Keypair } from '@mysten/sui/cryptography';
import { SuiGrpcClient } from '@mysten/sui/grpc';
import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';
import { Secp256k1Keypair } from '@mysten/sui/keypairs/secp256k1';
import { Secp256r1Keypair } from '@mysten/sui/keypairs/secp256r1';
import { Transaction } from '@mysten/sui/transactions';

const kp_rand_0 = new Ed25519Keypair();
const kp_rand_1 = new Secp256k1Keypair();
const kp_rand_2 = new Secp256r1Keypair();

const kp_import_0 = Ed25519Keypair.fromSecretKey(
fromHex('0xd463e11c7915945e86ac2b72d88b8190cfad8ff7b48e7eb892c275a5cf0a3e82'),
);
const kp_import_1 = Secp256k1Keypair.fromSecretKey(
fromHex('0xd463e11c7915945e86ac2b72d88b8190cfad8ff7b48e7eb892c275a5cf0a3e82'),
);
const kp_import_2 = Secp256r1Keypair.fromSecretKey(
fromHex('0xd463e11c7915945e86ac2b72d88b8190cfad8ff7b48e7eb892c275a5cf0a3e82'),
);

// $MNEMONICS는 wordlist에 있는 12/15/18/21/24개 단어를 가리키며, 예를 들어 "retire skin goose will hurry this field stadium drastic label husband venture cruel toe wire" 같은 형태이다. 자세한 내용은 [키와 주소](/guides/developer/transactions/transaction-auth/auth-overview#keys-and-addresses)를 참조한다.
const kp_derive_0 = Ed25519Keypair.deriveKeypair('$MNEMONICS');
const kp_derive_1 = Secp256k1Keypair.deriveKeypair('$MNEMONICS');
const kp_derive_2 = Secp256r1Keypair.deriveKeypair('$MNEMONICS');

const kp_derive_with_path_0 = Ed25519Keypair.deriveKeypair('$MNEMONICS', "m/44'/784'/1'/0'/0'");
const kp_derive_with_path_1 = Secp256k1Keypair.deriveKeypair('$MNEMONICS', "m/54'/784'/1'/0/0");
const kp_derive_with_path_2 = Secp256r1Keypair.deriveKeypair('$MNEMONICS', "m/74'/784'/1'/0/0");

// `kp_rand_0` 자리에 위 변수 이름들 중 하나를 사용한다.
const pk = kp_rand_0.getPublicKey();
const sender = pk.toSuiAddress();

// 예시 transaction을 생성한다.
const txb = new Transaction();
txb.setSender(sender);
txb.setGasPrice(5);
txb.setGasBudget(100);
const bytes = await txb.build();
const serializedSignature = (await keypair.signTransaction(bytes)).signature;

// 로컬에서 signature를 검증한다
expect(await keypair.getPublicKey().verifyTransaction(bytes, serializedSignature)).toEqual(true);

// 원하는 network용 sui client를 정의한다.
const client = new SuiGrpcClient({
baseUrl: 'https://fullnode.testnet.sui.io:443',
network: 'testnet',
});

// transaction을 실행한다.
let res = await client.executeTransaction({
transaction: bytes,
signatures: [serializedSignature],
});
console.log(res);

Notes

  1. 이 가이드는 단일 private key로 서명하는 방법을 보여준다. 더 복잡한 signing policy를 설정하는 것이 적절할 때는 다중 서명를 참조한다.
  2. 마찬가지로 native zkLogin은 위 단계를 따르지 않으므로, zkLogin address를 파생하고 ephemeral key pair로 zkLogin signature를 생성하는 방법을 이해하려면 zkLogin 통합을 참조한다.
  3. 이전 도구 대신 자체 signing mechanism을 구현하기로 했다면, 각 scheme에 대해 허용되는 signature specification은 서명 문서를 참조한다.
  4. Flag는 signature scheme을 구분하는 1 byte이다. 지원되는 scheme과 해당 flag는 서명에서 확인한다.
  5. execute_transaction_block endpoint는 signature 목록을 받으므로, sponsored transaction을 사용하지 않는 한 정확히 1개의 사용자 signature만 포함해야 하고, sponsored transaction을 쓰는 경우에는 gas object에 대한 두 번째 signature를 제공할 수 있다. 자세한 내용은 스폰서드 트랜잭션를 참조한다.