-
Notifications
You must be signed in to change notification settings - Fork 286
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
8969968
commit 1e1c413
Showing
15 changed files
with
270 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# Constants | ||
|
||
:::spl.memo.constants |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# Memo Program | ||
|
||
:::spl.memo.instructions |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
# Intro | ||
|
||
The Memo program is a simple program that validates a string of UTF-8 encoded | ||
characters and verifies that any accounts provided are signers of the | ||
transaction. The program also logs the memo, as well as any verified signer | ||
addresses, to the transaction log, so that anyone can easily observe memos and | ||
know they were approved by zero or more addresses by inspecting the transaction | ||
log from a trusted provider. | ||
|
||
## Background | ||
|
||
Solana's programming model and the definitions of the Solana terms used in this | ||
document are available at: | ||
|
||
- [https://docs.solana.com/apps](https://docs.solana.com/apps) | ||
- [https://docs.solana.com/terminology](https://docs.solana.com/terminology) | ||
|
||
## Source | ||
|
||
The Memo Program's source is available on | ||
[github](https://github.com/solana-labs/solana-program-library) | ||
|
||
## Interface | ||
|
||
The on-chain Memo Program is written in Rust and available on crates.io as | ||
[spl-memo](https://crates.io/crates/spl-memo) and | ||
[docs.rs](https://docs.rs/spl-memo). | ||
|
||
The crate provides a `build_memo()` method to easily create a properly | ||
constructed Instruction. | ||
|
||
## Operational Notes | ||
|
||
If zero accounts are provided to the signed-memo instruction, the program | ||
succeeds when the memo is valid UTF-8, and logs the memo to the transaction log. | ||
|
||
If one or more accounts are provided to the signed-memo instruction, all must be | ||
valid signers of the transaction for the instruction to succeed. | ||
|
||
### Logs | ||
|
||
This section details expected log output for memo instructions. | ||
|
||
Logging begins with entry into the program: | ||
`Program MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr invoke [1]` | ||
|
||
The program will include a separate log for each verified signer: | ||
`Program log: Signed by <BASE_58_ADDRESS>` | ||
|
||
Then the program logs the memo length and UTF-8 text: | ||
`Program log: Memo (len 4): "🐆"` | ||
|
||
If UTF-8 parsing fails, the program will log the failure point: | ||
`Program log: Invalid UTF-8, from byte 4` | ||
|
||
Logging ends with the status of the instruction, one of: | ||
`Program MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr success` | ||
`Program MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr failed: missing required signature for instruction` | ||
`Program MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr failed: invalid instruction data` | ||
|
||
For more information about exposing program logs on a node, head to the | ||
[developer | ||
docs](https://docs.solana.com/developing/on-chain-programs/debugging#logging) | ||
|
||
### Compute Limits | ||
|
||
Like all programs, the Memo Program is subject to the cluster's [compute | ||
budget](https://docs.solana.com/developing/programming-model/runtime#compute-budget). | ||
In Memo, compute is used for parsing UTF-8, verifying signers, and logging, | ||
limiting the memo length and number of signers that can be processed | ||
successfully in a single instruction. The longer or more complex the UTF-8 memo, | ||
the fewer signers can be supported, and vice versa. | ||
|
||
As of v1.5.1, an unsigned instruction can support single-byte UTF-8 of up to 566 | ||
bytes. An instruction with a simple memo of 32 bytes can support up to 12 | ||
signers. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
"""Client code for interacting with the Memo Program.""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
"""Memo program constants.""" | ||
from solana.publickey import PublicKey | ||
|
||
MEMO_PROGRAM_ID: PublicKey = PublicKey("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr") | ||
"""Public key that identifies the Memo program.""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
"""Memo program instructions.""" | ||
from __future__ import annotations | ||
|
||
from typing import NamedTuple | ||
|
||
from solana.publickey import PublicKey | ||
from solana.transaction import AccountMeta, TransactionInstruction | ||
|
||
|
||
class MemoParams(NamedTuple): | ||
"""Create memo transaction params.""" | ||
|
||
program_id: PublicKey | ||
"""Memo program account.""" | ||
signer: PublicKey | ||
"""Signing account.""" | ||
message: bytes | ||
"""Memo message in bytes.""" | ||
|
||
|
||
def decode_create_memo(instruction: TransactionInstruction) -> MemoParams: | ||
"""Decode a create_memo_instruction and retrieve the instruction params. | ||
Args: | ||
instruction: The instruction to decode. | ||
Returns: | ||
The decoded instruction. | ||
""" | ||
return MemoParams(signer=instruction.keys[0].pubkey, message=instruction.data, program_id=instruction.program_id) | ||
|
||
|
||
def create_memo(params: MemoParams) -> TransactionInstruction: | ||
"""Creates a transaction instruction that creates a memo. | ||
Message need to be encoded in bytes. | ||
Example: | ||
>>> signer, memo_program = PublicKey(1), PublicKey(2) | ||
>>> message = bytes("test", encoding="utf8") | ||
>>> params = MemoParams( | ||
... program_id=memo_program, | ||
... message=message, | ||
... signer=signer | ||
... ) | ||
>>> type(create_memo(params)) | ||
<class 'solana.transaction.TransactionInstruction'> | ||
Returns: | ||
The instruction to create a memo. | ||
""" | ||
keys = [ | ||
AccountMeta(pubkey=params.signer, is_signer=True, is_writable=True), | ||
] | ||
return TransactionInstruction( | ||
keys=keys, | ||
program_id=params.program_id, | ||
data=params.message, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
"""Tests for the Memo program.""" | ||
import pytest | ||
|
||
from solana.keypair import Keypair | ||
from solana.rpc.api import Client | ||
from solana.rpc.commitment import Finalized | ||
from solana.transaction import Transaction | ||
from spl.memo.constants import MEMO_PROGRAM_ID | ||
from spl.memo.instructions import MemoParams, create_memo | ||
|
||
from .utils import assert_valid_response | ||
|
||
|
||
@pytest.mark.integration | ||
def test_send_memo_in_transaction(stubbed_sender: Keypair, test_http_client: Client): | ||
"""Test sending a memo instruction to localnet.""" | ||
raw_message = "test" | ||
message = bytes(raw_message, encoding="utf8") | ||
# Create memo params | ||
memo_params = MemoParams( | ||
program_id=MEMO_PROGRAM_ID, | ||
signer=stubbed_sender.public_key, | ||
message=message, | ||
) | ||
# Create memo instruction | ||
memo_ix = create_memo(memo_params) | ||
# Create transfer tx to add memo to transaction from stubbed sender | ||
transfer_tx = Transaction().add(memo_ix) | ||
resp = test_http_client.send_transaction(transfer_tx, stubbed_sender) | ||
assert_valid_response(resp) | ||
txn_id = resp["result"] | ||
test_http_client.confirm_transaction(txn_id) | ||
resp2 = test_http_client.get_transaction(txn_id, commitment=Finalized, encoding="jsonParsed") | ||
log_message = resp2["result"]["meta"]["logMessages"][2].split('"') | ||
assert log_message[1] == raw_message | ||
assert resp2["result"]["transaction"]["message"]["instructions"][0]["parsed"] == raw_message | ||
assert resp2["result"]["transaction"]["message"]["instructions"][0]["programId"] == str(MEMO_PROGRAM_ID) | ||
|
||
|
||
@pytest.mark.integration | ||
def test_send_invalid_memo_in_memo_params(stubbed_sender: Keypair): | ||
"""Test creating a string message instead of bytes for the message.""" | ||
message = "test" | ||
with pytest.raises(TypeError): | ||
memo_params = MemoParams( | ||
program_id=MEMO_PROGRAM_ID, | ||
signer=stubbed_sender.public_key, | ||
message=message, | ||
) | ||
memo_ix = create_memo(memo_params) | ||
# The test will fail here. | ||
Transaction().add(memo_ix) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
from solana.keypair import Keypair | ||
from spl.memo.constants import MEMO_PROGRAM_ID | ||
from spl.memo.instructions import MemoParams, create_memo, decode_create_memo | ||
|
||
|
||
def test_memo(): | ||
"""Test creating a memo instruction.""" | ||
params = MemoParams(signer=Keypair().public_key, message=b"test", program_id=MEMO_PROGRAM_ID) | ||
assert decode_create_memo(create_memo(params)) == params |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters