Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 48 additions & 20 deletions pkg/arkade/asset_opcodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package arkade

import (
"crypto/sha256"
"encoding/binary"
"math/big"

"github.com/arkade-os/arkd/pkg/ark-lib/asset"
Expand Down Expand Up @@ -48,8 +49,10 @@ func opcodeInspectAssetGroupAssetId(op *opcode, data []byte, vm *Engine) error {
return nil
}

// opcodeInspectAssetGroupCtrl pops a group index k and pushes the control asset reference (txid, index).
// Pushes -1 if there is no control asset.
// opcodeInspectAssetGroupCtrl pops a group index k and pushes a tagged control
// asset reference.
// Found: pushes txid, index, 1.
// Missing: pushes empty txid, 0, 0.
func opcodeInspectAssetGroupCtrl(op *opcode, data []byte, vm *Engine) error {
if vm.assetPacket == nil {
return scriptError(txscript.ErrInvalidStackOperation, "no asset packet")
Expand All @@ -67,13 +70,16 @@ func opcodeInspectAssetGroupCtrl(op *opcode, data []byte, vm *Engine) error {
group := vm.assetPacket[int(k)]

if group.ControlAsset == nil {
vm.dstack.PushInt(-1)
vm.dstack.PushByteArray(nil)
vm.dstack.PushInt(0)
vm.dstack.PushInt(0)
return nil
}

if group.ControlAsset.Type == asset.AssetRefByID {
vm.dstack.PushByteArray(group.ControlAsset.AssetId.Txid[:])
vm.dstack.PushInt(scriptNum(group.ControlAsset.AssetId.Index))
vm.dstack.PushInt(1)
return nil
}

Expand All @@ -87,18 +93,22 @@ func opcodeInspectAssetGroupCtrl(op *opcode, data []byte, vm *Engine) error {
txHash := vm.tx.TxHash()
vm.dstack.PushByteArray(txHash[:])
vm.dstack.PushInt(scriptNum(group.ControlAsset.GroupIndex))
vm.dstack.PushInt(1)
} else {
vm.dstack.PushByteArray(ctrlGroup.AssetId.Txid[:])
vm.dstack.PushInt(scriptNum(ctrlGroup.AssetId.Index))
vm.dstack.PushInt(1)
}
return nil
}

return scriptError(txscript.ErrInvalidStackOperation, "invalid control asset type")
}

// opcodeFindAssetGroupByAssetId pops an asset ID (gidx, txid) and searches for the matching group index.
// Pushes the group index if found, or -1 if not found.
// opcodeFindAssetGroupByAssetId pops an asset ID (gidx, txid) and searches for
// the matching group index.
// Found: pushes group index, 1.
// Missing: pushes 0, 0.
func opcodeFindAssetGroupByAssetId(op *opcode, data []byte, vm *Engine) error {
if vm.assetPacket == nil {
return scriptError(txscript.ErrInvalidStackOperation, "no asset packet")
Expand Down Expand Up @@ -126,18 +136,21 @@ func opcodeFindAssetGroupByAssetId(op *opcode, data []byte, vm *Engine) error {
txHash := vm.tx.TxHash()
if txHash.IsEqual(&searchTxid) && scriptNum(i) == gidx {
vm.dstack.PushInt(scriptNum(i))
vm.dstack.PushInt(1)
return nil
}
continue
}

if group.AssetId.Txid.IsEqual(&searchTxid) && scriptNum(group.AssetId.Index) == gidx {
vm.dstack.PushInt(scriptNum(i))
vm.dstack.PushInt(1)
return nil
}
}

vm.dstack.PushInt(-1)
vm.dstack.PushInt(0)
vm.dstack.PushInt(0)
return nil
}

Expand Down Expand Up @@ -243,7 +256,7 @@ func opcodeInspectAssetGroup(op *opcode, data []byte, vm *Engine) error {
vm.dstack.PushByteArray(input.Txid[:])
}
vm.dstack.PushInt(scriptNum(input.Vin))
vm.dstack.PushInt(scriptNum(input.Amount))
pushAmountLE64(vm, input.Amount)

case 1:
if int(j) >= len(group.Outputs) || j < 0 {
Expand All @@ -253,7 +266,7 @@ func opcodeInspectAssetGroup(op *opcode, data []byte, vm *Engine) error {

vm.dstack.PushInt(1)
vm.dstack.PushInt(scriptNum(output.Vout))
vm.dstack.PushInt(scriptNum(output.Amount))
pushAmountLE64(vm, output.Amount)

default:
return scriptError(txscript.ErrInvalidStackOperation, "invalid source value")
Expand Down Expand Up @@ -290,24 +303,24 @@ func opcodeInspectAssetGroupSum(op *opcode, data []byte, vm *Engine) error {
if !sum.IsUint64() {
return scriptError(txscript.ErrInvalidStackOperation, "amount overflow")
}
vm.dstack.PushInt(scriptNum(sum.Uint64()))
pushAmountLE64(vm, sum.Uint64())
case 1:
sum := safeSumOutputs(group.Outputs)
if !sum.IsUint64() {
return scriptError(txscript.ErrInvalidStackOperation, "amount overflow")
}
vm.dstack.PushInt(scriptNum(sum.Uint64()))
pushAmountLE64(vm, sum.Uint64())
case 2:
inSum := safeSumInputs(group.Inputs)
if !inSum.IsUint64() {
return scriptError(txscript.ErrInvalidStackOperation, "amount overflow")
}
vm.dstack.PushInt(scriptNum(inSum.Uint64()))
pushAmountLE64(vm, inSum.Uint64())
outSum := safeSumOutputs(group.Outputs)
if !outSum.IsUint64() {
return scriptError(txscript.ErrInvalidStackOperation, "amount overflow")
}
vm.dstack.PushInt(scriptNum(outSum.Uint64()))
pushAmountLE64(vm, outSum.Uint64())
default:
return scriptError(txscript.ErrInvalidStackOperation, "invalid source value")
}
Expand Down Expand Up @@ -374,7 +387,7 @@ func opcodeInspectOutAssetAt(op *opcode, data []byte, vm *Engine) error {
if scriptNum(idx) == t {
vm.dstack.PushByteArray(assetTxid[:])
vm.dstack.PushInt(scriptNum(gidx))
vm.dstack.PushInt(scriptNum(output.Amount))
pushAmountLE64(vm, output.Amount)
return nil
}
idx++
Expand All @@ -386,7 +399,8 @@ func opcodeInspectOutAssetAt(op *opcode, data []byte, vm *Engine) error {
}

// opcodeInspectOutAssetLookup pops gidx, txid, and output index o, then looks up the asset amount.
// Pushes the amount if found, or -1 if not found.
// Found: pushes the amount as 8-byte LE64, then 1 (success flag).
// Not found: pushes 0 as 8-byte LE64, then 0 (failure flag).
func opcodeInspectOutAssetLookup(op *opcode, data []byte, vm *Engine) error {
if vm.assetPacket == nil {
return scriptError(txscript.ErrInvalidStackOperation, "no asset packet")
Expand Down Expand Up @@ -434,13 +448,15 @@ func opcodeInspectOutAssetLookup(op *opcode, data []byte, vm *Engine) error {

for _, output := range group.Outputs {
if uint32(output.Vout) == uint32(o) {
vm.dstack.PushInt(scriptNum(output.Amount))
pushAmountLE64(vm, output.Amount)
vm.dstack.PushInt(1)
return nil
}
}
}

vm.dstack.PushInt(-1)
vm.dstack.PushByteArray(make([]byte, 8))
vm.dstack.PushInt(0)
return nil
}

Expand Down Expand Up @@ -511,7 +527,7 @@ func opcodeInspectInAssetAt(op *opcode, data []byte, vm *Engine) error {

vm.dstack.PushByteArray(inputTxid[:])
vm.dstack.PushInt(scriptNum(gidx))
vm.dstack.PushInt(scriptNum(input.Amount))
pushAmountLE64(vm, input.Amount)
return nil
}
idx++
Expand All @@ -523,7 +539,8 @@ func opcodeInspectInAssetAt(op *opcode, data []byte, vm *Engine) error {
}

// opcodeInspectInAssetLookup pops gidx, txid, and input index i, then looks up the asset amount.
// Pushes the amount if found, or -1 if not found.
// Found: pushes the amount as 8-byte LE64, then 1 (success flag).
// Not found: pushes 0 as 8-byte LE64, then 0 (failure flag).
func opcodeInspectInAssetLookup(op *opcode, data []byte, vm *Engine) error {
if vm.assetPacket == nil {
return scriptError(txscript.ErrInvalidStackOperation, "no asset packet")
Expand Down Expand Up @@ -578,13 +595,15 @@ func opcodeInspectInAssetLookup(op *opcode, data []byte, vm *Engine) error {
}

if inputTxid.IsEqual(&searchTxid) {
vm.dstack.PushInt(scriptNum(input.Amount))
pushAmountLE64(vm, input.Amount)
vm.dstack.PushInt(1)
return nil
}
}
}

vm.dstack.PushInt(-1)
vm.dstack.PushByteArray(make([]byte, 8))
vm.dstack.PushInt(0)
return nil
}

Expand Down Expand Up @@ -639,3 +658,12 @@ func safeSumOutputs(outputs []asset.AssetOutput) *big.Int {
}
return sum
}

// pushAmountLE64 encodes a uint64 as 8-byte little-endian and pushes it onto
// the data stack. This is consistent with how OP_INSPECTOUTPUTVALUE pushes
// satoshi values, allowing scripts to use 64-bit arithmetic ops directly.
func pushAmountLE64(vm *Engine, amount uint64) {
buf := make([]byte, 8)
binary.LittleEndian.PutUint64(buf, amount)
vm.dstack.PushByteArray(buf)
}
Loading
Loading