1+ #! /bin/bash
2+
3+ # #################################################
4+
5+ # Default configuration values
6+
7+ # #################################################
8+
9+ # Exit immediately if a command exits with a non-zero status,
10+ # treat unset variables as an error, and fail if any command in a pipeline fails
11+ set -euo pipefail
12+
13+ # Colors
14+ # BLACK='\033[0;30m'
15+ RED=' \033[0;31m'
16+ GREEN=' \033[0;32m'
17+ YELLOW=' \033[0;33m'
18+ BLUE=' \033[0;34m'
19+ CYAN=' \033[0;36m'
20+ BRIGHTWHITE=' \033[0;37;1m'
21+ NC=' \033[0m'
22+
23+ # Check if cardano-cli is installed
24+ if ! command -v cardano-cli > /dev/null 2>&1 ; then
25+ echo " Error: cardano-cli is not installed or not in your PATH." >&2
26+ exit 1
27+ fi
28+
29+ # Check if ipfs cli is installed
30+ if ! command -v ipfs > /dev/null 2>&1 ; then
31+ echo " Error: ipfs cli is not installed or not in your PATH." >&2
32+ exit 1
33+ fi
34+
35+ # Usage message
36+
37+ usage () {
38+ echo " Usage: $0 <jsonld-file> [--deposit-return-addr <stake address>]"
39+ echo " Options:"
40+ echo " <jsonld-file> Path to the JSON-LD metadata file"
41+ echo " --deposit-return-addr <stake address> Check that metadata deposit return address matches provided one (Bech32)"
42+ echo " -h, --help Show this help message and exit"
43+ exit 1
44+ }
45+
46+ # Initialize variables with defaults
47+ input_file=" "
48+
49+ # Optional variables
50+ deposit_return_address_input=" "
51+
52+ # Parse command line arguments
53+ while [[ $# -gt 0 ]]; do
54+ case $1 in
55+ --deposit-return-addr)
56+ if [ -n " ${2:- } " ]; then
57+ deposit_return_address_input=" $2 "
58+ shift 2
59+ else
60+ echo -e " ${RED} Error: --deposit-return-addr requires a value${NC} " >&2
61+ usage
62+ fi
63+ ;;
64+ -h|--help)
65+ usage
66+ ;;
67+ * )
68+ if [ -z " $input_file " ]; then
69+ input_file=" $1 "
70+ else
71+ echo -e " ${RED} Error: Input file already specified. Unexpected argument: $1 ${NC} " >&2
72+ usage
73+ fi
74+ shift
75+ ;;
76+ esac
77+ done
78+
79+ # If no input file provided, show usage
80+ if [ -z " $input_file " ]; then
81+ echo -e " ${RED} Error: No input file specified${NC} " >&2
82+ usage
83+ fi
84+
85+ # Check if input file exists
86+ if [ ! -f " $input_file " ]; then
87+ echo -e " ${RED} Error: Input file not found: $input_file ${NC} " >&2
88+ exit 1
89+ fi
90+
91+ echo -e " "
92+ echo -e " ${YELLOW} Creating an Info governance action from a given metadata file${NC} "
93+ echo -e " ${CYAN} This script assumes compliance Intersect's Info action schema${NC} "
94+ echo -e " ${CYAN} This script assumes that CARDANO_NODE_SOCKET_PATH, CARDANO_NODE_NETWORK_ID and IPFS_GATEWAY_URI are set${NC} "
95+
96+ # Exit if socket path is not set
97+ if [ -z " $CARDANO_NODE_SOCKET_PATH " ]; then
98+ echo " Error: Cardano node $CARDANO_NODE_SOCKET_PATH environment variable is not set." >&2
99+ exit 1
100+ fi
101+
102+ # Exit if network id is not set
103+ if [ -z " $CARDANO_NODE_NETWORK_ID " ]; then
104+ echo " Error: Cardano node $CARDANO_NODE_NETWORK_ID environment variable is not set." >&2
105+ fi
106+
107+ # Get if mainnet or testnet
108+ if [ " $CARDANO_NODE_NETWORK_ID " = " 764824073" ] || [ " $CARDANO_NODE_NETWORK_ID " = " mainnet" ]; then
109+ echo -e " ${YELLOW} Local node is using mainnet${NC} "
110+ protocol_magic=" mainnet"
111+ else
112+ echo -e " ${YELLOW} Local node is using a testnet${NC} "
113+ protocol_magic=" testnet"
114+ fi
115+
116+ # Open the provided metadata file
117+
118+ # Do some basic validation checks on metadata
119+ echo -e " "
120+ echo -e " ${CYAN} Doing some basic validation and checks on metadata${NC} "
121+
122+ # Function to check if jq query returned null or empty
123+ check_field () {
124+ local field_name=" $1 "
125+ local field_value=" $2 "
126+
127+ if [ -z " $field_value " ] || [ " $field_value " = " null" ]; then
128+ echo -e " ${RED} Error: Required field '$field_name ' not found in metadata${NC} " >&2
129+ exit 1
130+ fi
131+ }
132+
133+ # Extract and validate required fields
134+ title=$( jq -r ' .body.title' " $input_file " )
135+ check_field " title" " $title "
136+
137+ ga_type=$( jq -r ' .body.onChain.governanceActionType' " $input_file " )
138+ check_field " governanceActionType" " $ga_type "
139+
140+ deposit_return=$( jq -r ' .body.onChain.depositReturnAddress' " $input_file " )
141+ check_field " depositReturnAddress" " $deposit_return "
142+
143+ authors=$( jq -r ' .authors' " $input_file " )
144+ check_field " authors" " $authors "
145+ witness=$( jq -r ' .authors[0].witness' " $input_file " )
146+ check_field " witness" " $witness "
147+
148+ if [ " $ga_type " = " info" ]; then
149+ echo " Metadata has correct governanceActionType"
150+ else
151+ echo " Metadata does not have the correct governanceActionType"
152+ echo " Expected: info found: $ga_type "
153+ exit 1
154+ fi
155+
156+ # if return address passed in check against metadata
157+ if [ ! -z " $deposit_return_address_input " ]; then
158+ echo " Deposit return address provided"
159+ echo " Comparing provided address to metadata"
160+ if [ " $deposit_return_address_input " = " $deposit_return " ]; then
161+ echo -e " ${GREEN} Metadata has expected deposit return address${NC} "
162+ else
163+ echo -e " ${RED} Metadata does not have expected deposit return address${NC} "
164+ exit 1
165+ fi
166+ fi
167+
168+ # use bech32 prefix to determine if addresses are mainnet or testnet
169+ is_stake_address_mainnet () {
170+ local address=" $1 "
171+ # Check if address starts with stake1 (mainnet)
172+ if [[ " $address " =~ ^stake1 ]]; then
173+ return 0
174+ # Check if address starts with stake_test1 (testnet)
175+ elif [[ " $address " =~ ^stake_test1 ]]; then
176+ return 1
177+ else
178+ echo -e " ${RED} Error: Invalid stake address format: $address ${NC} " >&2
179+ exit 1
180+ fi
181+ }
182+
183+ # if mainnet node then expect addresses to be mainnet
184+ if [ " $protocol_magic " = " mainnet" ]; then
185+ if is_stake_address_mainnet " $deposit_return " ; then
186+ echo -e " Deposit return address is a valid mainnet stake address"
187+ else
188+ echo -e " ${RED} Deposit return address is not a valid mainnet stake address${NC} "
189+ exit 1
190+ fi
191+ else
192+ if ! is_stake_address_mainnet " $deposit_return " ; then
193+ echo -e " Deposit return address is a valid testnet stake address"
194+ else
195+ echo -e " ${RED} Deposit return address is not a valid testnet stake address${NC} "
196+ exit 1
197+ fi
198+ fi
199+
200+ # use header byte to determine if stake address is script-based or key-based
201+ is_stake_address_script () {
202+ local address=" $1 "
203+
204+ address_hex=$( cardano-cli address info --address " $address " | jq -r " .base16" )
205+ first_char=" ${address_hex: 0: 1} "
206+
207+ if [ " $first_char " = " f" ]; then
208+ return 0 # true
209+ elif [ " $first_char " = " e" ]; then
210+ return 1 # false
211+ else
212+ echo -e " ${RED} Error: Invalid stake address header byte${NC} " >&2
213+ exit 1
214+ fi
215+ }
216+
217+ is_stake_address_registered (){
218+ local address=" $1 "
219+ echo -e " Checking if stake address $address is registered on-chain..."
220+ which cardano-cli
221+ cardano-cli conway query stake-address-info --address " $address "
222+ stake_address_deposit=$( cardano-cli conway query stake-address-info --address " $address " | jq -r ' .[0].delegationDeposit' )
223+
224+ if [ " $stake_address_deposit " != " null" ]; then
225+ return 0
226+ else
227+ return 1
228+ fi
229+ }
230+
231+ # check if stake addresses are registered
232+ if is_stake_address_registered " $deposit_return " ; then
233+ echo -e " Deposit return stake address is registered"
234+ else
235+ echo -e " ${RED} Deposit return stake address is not registered, exiting.${NC} "
236+ exit 1
237+ fi
238+
239+
240+ echo -e " ${GREEN} Automatic validations passed${NC} "
241+ echo -e " "
242+ echo -e " ${CYAN} Computing details${NC} "
243+
244+ # Compute the hash and IPFS URI
245+ file_hash=$( b2sum -l 256 " $input_file " | awk ' {print $1}' )
246+ echo -e " Metadata file hash: ${YELLOW} $file_hash ${NC} "
247+
248+ ipfs_cid=$( ipfs add -Q --cid-version 1 " $input_file " )
249+ echo -e " IPFS URI: ${YELLOW} ipfs://$ipfs_cid ${NC} "
250+
251+ # Make user manually confirm the choices
252+ echo -e " "
253+ echo -e " ${CYAN} Creating info action${NC} "
254+ echo -e " Title: ${YELLOW} $title ${NC} "
255+ echo -e " "
256+ echo -e " Deposit return address: ${YELLOW} $deposit_return ${NC} "
257+ if is_stake_address_script " $deposit_return " ; then
258+ echo -e " (this is a script-based address)"
259+ else
260+ echo -e " (this is a key-based address)"
261+ fi
262+
263+ echo -e " "
264+ read -p " Do you want to proceed with this deposit return address? (yes/no): " confirm_deposit
265+
266+ if [ " $confirm_deposit " != " yes" ]; then
267+ echo -e " ${RED} Deposit address not confirmed by user, exiting.${NC} "
268+ exit 1
269+ fi
270+
271+ # Create the action
272+ echo -e " "
273+ echo -e " ${CYAN} Creating action file...${NC} "
274+
275+ cardano-cli conway governance action create-info \
276+ --$protocol_magic \
277+ --governance-action-deposit $( cardano-cli conway query gov-state | jq -r ' .currentPParams.govActionDeposit' ) \
278+ --deposit-return-stake-address " $deposit_return " \
279+ --anchor-url " ipfs://$ipfs_cid " \
280+ --anchor-data-hash " $file_hash " \
281+ --check-anchor-data \
282+ --out-file " $input_file .action"
283+
284+ echo -e " ${GREEN} Action file created at " $input_file .action" ${NC} "
285+
286+ echo -e " "
287+ echo -e " ${CYAN} Creating JSON representation of action file...${NC} "
288+
289+ cardano-cli conway governance action view --action-file " $input_file .action" > " $input_file .action.json"
290+ echo -e " ${GREEN} JSON file created at " $input_file .action.json" ${NC} "
0 commit comments