Skip to content

Commit

Permalink
feat: modified bitcoin_transaction_script to infer flags (#278)
Browse files Browse the repository at this point in the history
<!-- enter the gh issue after hash -->

- [ ] resolves
#277
- [ ] follows contribution
[guide](https://github.com/keep-starknet-strange/shinigami/blob/main/CONTRIBUTING.md)
- [ ] code change includes tests

<!-- PR description below -->
  • Loading branch information
okhaimie-dev authored Nov 13, 2024
1 parent 273dd0a commit b52043c
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 37 deletions.
18 changes: 16 additions & 2 deletions packages/cmds/src/main.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -203,14 +203,27 @@ fn backend_debug(input: InputData) -> u8 {
#[derive(Drop)]
struct ValidateRawInput {
raw_transaction: ByteArray,
utxo_hints: Array<UTXO>
utxo_hints: Array<UTXO>,
flags: ByteArray,
}

fn run_raw_transaction(mut input: ValidateRawInput) -> u8 {
println!("Running Bitcoin Script with raw transaction: '{}'", input.raw_transaction);
let raw_transaction = hex_to_bytecode(@input.raw_transaction);
let transaction = EngineInternalTransactionTrait::deserialize(raw_transaction);

// Parse the flags
let script_flags = flags::parse_flags(input.flags);
println!("Script flags: {}", script_flags);

// For coinbase transactions, we expect no UTXO hints since it creates new coins
if input.utxo_hints.is_empty() {
println!("Potential coinbase transaction detected - skipping validation");
return 1;
}

let mut utxo_hints = array![];

for hint in input
.utxo_hints
.span() {
Expand All @@ -225,7 +238,8 @@ fn run_raw_transaction(mut input: ValidateRawInput) -> u8 {
}
);
};
let res = validate::validate_transaction(@transaction, 0, utxo_hints);

let res = validate::validate_transaction(@transaction, script_flags, utxo_hints);
match res {
Result::Ok(_) => {
println!("Execution successful");
Expand Down
42 changes: 42 additions & 0 deletions scripts/get_tx_from_block.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#!/bin/bash
#
# This script fetches a transaction hash using block height and transaction index
# Usage: ./get_tx_from_block.sh <block_height> <tx_index>

if [ "$#" -ne 2 ]; then
echo "Usage: $0 <block_height> <tx_index>"
echo "Example: $0 869322 1"
exit 1
fi

BLOCK_HEIGHT=$1
TX_INDEX=$2

RPC_API="https://bitcoin-mainnet.public.blastapi.io"

# First, get the block hash for the given height
BLOCK_HASH_RES=$(curl -s -X POST -H "Content-Type: application/json" \
-d "{\"jsonrpc\":\"1.0\",\"id\":0,\"method\":\"getblockhash\",\"params\":[$BLOCK_HEIGHT]}" \
$RPC_API)

BLOCK_HASH=$(echo $BLOCK_HASH_RES | jq -r '.result')

if [ -z "$BLOCK_HASH" ] || [ "$BLOCK_HASH" = "null" ]; then
echo "Error: Could not fetch block hash for height $BLOCK_HEIGHT"
exit 1
fi

# Then, get the block data which includes all transaction hashes
BLOCK_DATA_RES=$(curl -s -X POST -H "Content-Type: application/json" \
-d "{\"jsonrpc\":\"1.0\",\"id\":0,\"method\":\"getblock\",\"params\":[\"$BLOCK_HASH\"]}" \
$RPC_API)

# Extract the transaction hash at the specified index
TX_HASH=$(echo $BLOCK_DATA_RES | jq -r ".result.tx[$TX_INDEX]")

if [ -z "$TX_HASH" ] || [ "$TX_HASH" = "null" ]; then
echo "Error: Could not find transaction at index $TX_INDEX in block $BLOCK_HEIGHT"
exit 1
fi

echo $TX_HASH
139 changes: 104 additions & 35 deletions scripts/run_bitcoin_transaction.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
BASE_DIR=$SCRIPT_DIR/..

# BIP activation heights (mainnet)
BIP_16_BLOCK_HEIGHT=173805 # P2SH
BIP_66_BLOCK_HEIGHT=363725 # Strict DER signatures
BIP_65_BLOCK_HEIGHT=388381 # CHECKLOCKTIMEVERIFY
BIP_112_BLOCK_HEIGHT=419328 # CHECKSEQUENCEVERIFY
BIP_141_BLOCK_HEIGHT=481824 # SegWit
BIP_341_BLOCK_HEIGHT=709632 # Taproot

TXID=$1

RPC_API="https://bitcoin-mainnet.public.blastapi.io"
Expand All @@ -16,8 +24,8 @@ RPC_API="https://bitcoin-mainnet.public.blastapi.io"
# pub amount: i64,
# pub pubkey_script: ByteArray,
# pub block_height: i32,
# // TODO: flags?
#}
# pub flags: ByteArray,
#}

# Fetch the transaction
RES=$(curl -s -X POST -H "Content-Type: application/json" -d "{\"jsonrpc\":\"1.0\",\"id\":0,\"method\":\"getrawtransaction\",\"params\":[\"$TXID\", true]}" $RPC_API)
Expand All @@ -34,40 +42,101 @@ RAW_TX_INPUT=$(sed 's/^\[\(.*\)\]$/\1/' <<< $RAW_TX_TEXT)
VINS=$(echo $RES | jq '.result.vin')
# echo "VINS: $VINS"

BLOCK_HEIGHT=0 # TODO?
AMOUNT=0 # TODO?
UTXOS=""
for vin in $(echo $VINS | jq -r '.[] | @base64'); do
_jq() {
echo ${vin} | base64 --decode | jq -r ${1}
}

TXID=$(echo $(_jq '.txid'))
VOUT=$(echo $(_jq '.vout'))

# Fetch the transaction
RES=$(curl -s -X POST -H "Content-Type: application/json" -d "{\"jsonrpc\":\"1.0\",\"id\":0,\"method\":\"getrawtransaction\",\"params\":[\"$TXID\", true]}" $RPC_API)

# AMOUNT=$(echo $RES | jq ".result.vout[$VOUT].value")
# echo "AMOUNT: $AMOUNT"
# Get block hash from transaction
BLOCK_HASH=$(echo $RES | jq -r '.result.blockhash')
# Fetch block info to get height
BLOCK_INFO=$(curl -s -X POST -H "Content-Type: application/json" -d "{\"jsonrpc\":\"1.0\",\"id\":0,\"method\":\"getblock\",\"params\":[\"$BLOCK_HASH\"]}" $RPC_API)
BLOCK_HEIGHT=$(echo $BLOCK_INFO | jq -r '.result.height')
echo "BLOCK_HEIGHT: $BLOCK_HEIGHT"
BLOCK_VERSION=$(echo $BLOCK_INFO | jq -r '.result.version')

# Check if this is a coinbase transaction
IS_COINBASE=false
VIN_TXID=$(echo $RES | jq -r '.result.vin[0].txid')
if [ "$VIN_TXID" = "null" ] || [ "$VIN_TXID" = "0000000000000000000000000000000000000000000000000000000000000000" ]; then
IS_COINBASE=true
echo "Detected coinbase transaction"
FLAGS="" # Empty flags for coinbase
else
# Initialize empty flags string
FLAGS=""

# BIP16 (P2SH)
if [ $BLOCK_HEIGHT -ge $BIP_16_BLOCK_HEIGHT ]; then
FLAGS="P2SH"
fi

# BIP66 (Strict DER signatures)
if [ $BLOCK_VERSION -ge 3 ] && [ $BLOCK_HEIGHT -ge $BIP_66_BLOCK_HEIGHT ]; then
[ -n "$FLAGS" ] && FLAGS="$FLAGS,"
FLAGS="${FLAGS}DERSIG"
fi

# BIP65 (CHECKLOCKTIMEVERIFY)
if [ $BLOCK_VERSION -ge 4 ] && [ $BLOCK_HEIGHT -ge $BIP_65_BLOCK_HEIGHT ]; then
[ -n "$FLAGS" ] && FLAGS="$FLAGS,"
FLAGS="${FLAGS}CHECKLOCKTIMEVERIFY"
fi

# BIP112 (CHECKSEQUENCEVERIFY)
if [ $BLOCK_HEIGHT -ge $BIP_112_BLOCK_HEIGHT ]; then
[ -n "$FLAGS" ] && FLAGS="$FLAGS,"
FLAGS="${FLAGS}CHECKSEQUENCEVERIFY"
fi

# BIP141 (SegWit)
if [ $BLOCK_HEIGHT -ge $BIP_141_BLOCK_HEIGHT ]; then
[ -n "$FLAGS" ] && FLAGS="$FLAGS,"
FLAGS="${FLAGS}WITNESS"
FLAGS="${FLAGS},NULLDUMMY"
fi

# BIP341 (Taproot)
if [ $BLOCK_HEIGHT -ge $BIP_341_BLOCK_HEIGHT ]; then
[ -n "$FLAGS" ] && FLAGS="$FLAGS,"
FLAGS="${FLAGS}TAPROOT"
fi
fi

echo "Transaction type: $([ "$IS_COINBASE" = true ] && echo 'Coinbase' || echo 'Regular')"
echo "Block height: $BLOCK_HEIGHT"
echo "Script flags: $FLAGS"
echo "UTXO construction: $UTXOS"

PUBKEY_SCRIPT=$(echo $RES | jq ".result.vout[$VOUT].scriptPubKey.hex" | tr -d '"')
PUBKEY_SCRIPT="0x$PUBKEY_SCRIPT"
# echo "PUBKEY_SCRIPT: $PUBKEY_SCRIPT"

PUBKEY_SCRIPT_TEXT=$($SCRIPT_DIR/text_to_byte_array.sh $PUBKEY_SCRIPT)
PUBKEY_SCRIPT_INPUT=$(sed 's/^\[\(.*\)\]$/\1/' <<< $PUBKEY_SCRIPT_TEXT)
# echo "PUBKEY_SCRIPT_INPUT: $PUBKEY_SCRIPT_INPUT"

# Construct UTXO
UTXO="{\"amount\":$AMOUNT,\"pubkey_script\":\"$PUBKEY_SCRIPT\",\"block_height\":$BLOCK_HEIGHT}"
# echo "UTXO: $UTXO"

UTXOS="$UTXOS$AMOUNT,$PUBKEY_SCRIPT_INPUT,$BLOCK_HEIGHT,"
done
UTXOS=$(sed 's/,$//' <<< $UTXOS)

JOINED_INPUT="[$RAW_TX_INPUT,[$UTXOS]]"
AMOUNT=0 # TODO?
if [ "$IS_COINBASE" = true ]; then
echo "Setting up empty UTXO list for coinbase transaction"
# Leave UTXOS empty for coinbase transactions
UTXOS=""
else
UTXOS=""
for vin in $(echo $VINS | jq -r '.[] | @base64'); do
_jq() {
echo ${vin} | base64 --decode | jq -r ${1}
}

TXID=$(echo $(_jq '.txid'))
VOUT=$(echo $(_jq '.vout'))

# Fetch the transaction
RES=$(curl -s -X POST -H "Content-Type: application/json" -d "{\"jsonrpc\":\"1.0\",\"id\":0,\"method\":\"getrawtransaction\",\"params\":[\"$TXID\", true]}" $RPC_API)

AMOUNT=$(echo $RES | jq ".result.vout[$VOUT].value * 100000000 | floor")
PUBKEY_SCRIPT=$(echo $RES | jq ".result.vout[$VOUT].scriptPubKey.hex" | tr -d '"')
PUBKEY_SCRIPT="0x$PUBKEY_SCRIPT"

PUBKEY_SCRIPT_TEXT=$($SCRIPT_DIR/text_to_byte_array.sh $PUBKEY_SCRIPT)
PUBKEY_SCRIPT_INPUT=$(sed 's/^\[\(.*\)\]$/\1/' <<< $PUBKEY_SCRIPT_TEXT)

UTXOS="$UTXOS$AMOUNT,$PUBKEY_SCRIPT_INPUT,$BLOCK_HEIGHT"
done
UTXOS=$(sed 's/,$//' <<< $UTXOS)
fi

FLAGS_TEXT=$($SCRIPT_DIR/text_to_byte_array.sh "$FLAGS")
FLAGS_INPUT=$(sed 's/^\[\(.*\)\]$/\1/' <<< $FLAGS_TEXT)

JOINED_INPUT="[$RAW_TX_INPUT,[$UTXOS],$FLAGS_INPUT]"
# echo "JOINED_INPUT: $JOINED_INPUT"

echo "scarb cairo-run --package shinigami_cmds --function run_raw_transaction \"$JOINED_INPUT\""
Expand Down

0 comments on commit b52043c

Please sign in to comment.