毎月のエンロールメントがめんどくさい
ってことで、chatGPTさんというか、perplexityに相談。
もう、どうやって構築していったのかは、忘れたけれど、Pythonが得意らしいので以下のスクリプトを組んでもらった。
各個人の環境で動くかどうかは分かりません。動かないときは都度、AIさんにエラー見せて対応方法を提示してもらったと思う。
※秘密鍵が必要なので、原則的にセキュリティには気を付けてください。
#!/usr/bin/env python3
import random
from symbolchain.nem.KeyPair import KeyPair
from symbolchain.facade.NemFacade import NemFacade
from symbolchain.CryptoTypes import PrivateKey
from symbolchain.nem.Network import NetworkTimestamp
from binascii import unhexlify
import requests
import json
facade = NemFacade('mainnet')
# Sender data: private key and nodehost (replace with your actual data)
sender_data = [
{
"private_key": PrivateKey(unhexlify('****************************************************************')),
"nodehost": "nem01a.symbol-node.com"
},
{
"private_key": PrivateKey(unhexlify('****************************************************************')),
"nodehost": "nem02.symbol-node.com"
},
{
"private_key": PrivateKey(unhexlify('****************************************************************')),
"nodehost": "nem03.symbol-node.com"
},
]
# Multiple node URLs
node_urls = [
'https://nem01a.symbol-node.com:7891',
'https://nem02.symbol-node.com:7891',
'https://nem03.symbol-node.com:7891',
'https://nem04.symbol-node.com:7891',
'https://nem05.symbol-node.com:7891',
'https://nem06.symbol-node.com:7891',
'https://nem01.neluta.win:7891',
'https://nem02.neluta.win:7891',
'https://nem03.neluta.win:7891',
'https://nem04.neluta.win:7891',
'https://nem05.neluta.win:7891',
]
def get_random_node():
return random.choice(node_urls)
def get_codeword(public_key):
url = f"https://nem.io/supernode/api/codeword/{public_key}"
response = requests.get(url)
if response.status_code == 200:
return response.text.strip()
else:
raise Exception(f"Failed to get codeword for public key {public_key}")
def send_transaction(sender_private_key, recipient_address, amount, message):
node_url = get_random_node()
post_string = f"{node_url}/transaction/announce"
time_string = f"{node_url}/time-sync/network-time"
# Obtain network time
networkTimeRequest = requests.get(time_string)
networkTime = NetworkTimestamp(int(networkTimeRequest.json()['sendTimeStamp'] / 1000))
# Set the deadline to 1 hour in the future
deadline = networkTime.add_hours(1).timestamp
senderKeypair = NemFacade.KeyPair(sender_private_key)
senderPubkey = senderKeypair.public_key
senderAddress = facade.network.public_key_to_address(senderPubkey)
# Create a transfer transaction
transfer = facade.transaction_factory.create({
'type': 'transfer_transaction_v1',
'signer_public_key': senderPubkey,
'recipient_address': recipient_address,
'deadline': deadline,
'message': {
'message_type': 'plain',
'message': message
},
'amount': amount,
'fee': 200000, # 0.2 XEM fee
'timestamp': networkTime.timestamp
})
# Sign the transaction and attach the signature
signature = facade.sign_transaction(senderKeypair, transfer)
tx = facade.transaction_factory.attach_signature(transfer, signature)
# Announce the transaction to the network
r = requests.post(post_string, json=json.loads(tx))
return r, node_url
def send_transaction_with_retry(sender_private_key, recipient_address, amount, message, max_retries=3):
for attempt in range(max_retries):
try:
response, used_node = send_transaction(sender_private_key, recipient_address, amount, message)
if response.status_code == 200:
return response, used_node
else:
print(f"Attempt {attempt + 1} failed. Status code: {response.status_code}")
except Exception as e:
print(f"試行回数 {attempt + 1} : {str(e)}")
if attempt < max_retries - 1:
print("別のノードへ接続をリトライ...")
raise Exception("Failed to send transaction after maximum retries")
def main():
# Ask for the recipient address
recipient_address = input("エンロールメントアドレスを入力してください。: ").strip()
# Validate the recipient address (you may want to add more robust validation)
if not recipient_address or len(recipient_address) != 40:
print("無効な受信者アドレスです。有効なNEMアドレスを入力してください。")
return
print(f"エンロールメントアドレスへ送信: {recipient_address}")
print("-------------------------------------------")
# Send transactions from multiple senders
for i, sender in enumerate(sender_data):
sender_key = sender["private_key"]
nodehost = sender["nodehost"]
# Derive public key from private key
key_pair = KeyPair(sender_key)
public_key = key_pair.public_key
# Get the codeword directly from the API
codeword = get_codeword(public_key)
# Create the message
message = f"enroll {nodehost} {codeword}"
amount = 0 # Sending 0 XEM
try:
response, used_node = send_transaction_with_retry(sender_key, recipient_address, amount, message)
print(f"Transaction {i+1}:")
print(f" Status Code: {response.status_code}")
print(f" Response: {response.json()}")
print(f" 送信メッセージ: {message}")
print(f" 使用ノード: {used_node}")
except Exception as e:
print(f"Transaction {i+1} {nodehost} リトライに失敗: {str(e)}")
print()
if __name__ == "__main__":
main()
以下の部分の*****部分が秘密鍵です。で、登録するhost名か、IPアドレスを間違わないように記述してください。このセットでいくつでも追加できます。
※ここの秘密鍵はアドレス自身の秘密鍵です。委任秘密鍵ではないです。
{
"private_key": PrivateKey(unhexlify('****************************************************************')),
"nodehost": "nem01a.symbol-node.com"
},
以下の部分は送信時に接続するノードです。複数にしているのは負荷分散の為。どこのノードでもいいですよ。
# Multiple node URLs
node_urls = [
'https://nem01a.symbol-node.com:7891',
'https://nem02.symbol-node.com:7891',
'https://nem03.symbol-node.com:7891',
'https://nem04.symbol-node.com:7891',
'https://nem05.symbol-node.com:7891',
'https://nem06.symbol-node.com:7891',
'https://nem01.neluta.win:7891',
'https://nem02.neluta.win:7891',
'https://nem03.neluta.win:7891',
'https://nem04.neluta.win:7891',
'https://nem05.neluta.win:7891',
]
スクリプトを実行すると、エンロールメント用のアドレスを聞いてきますので、最新のアドレスを入力し、エンターで実行されます。
※あと、手数料を0.2xem固定にしてますが、host名が長いと足らないかもです。
ということで、月一回のイベントですがこれで楽ができます。
くれぐれも言いますが、セキュリティは本当に気を付けてください。秘密鍵が平文で羅列してるだけですので。何か良い方法があれば教えて。
私は、外部からはアクセスできないサーバー内より実行してます。