github.com/algorand/go-algorand-sdk@v1.24.0/templates/hashTimeLockedContract.go (about)

     1  package templates
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/sha256"
     6  	"encoding/base64"
     7  	"fmt"
     8  
     9  	"github.com/algorand/go-algorand-sdk/crypto"
    10  	"github.com/algorand/go-algorand-sdk/logic"
    11  	"github.com/algorand/go-algorand-sdk/types"
    12  	"golang.org/x/crypto/sha3"
    13  )
    14  
    15  // HTLC template representation
    16  type HTLC struct {
    17  	ContractTemplate
    18  }
    19  
    20  // MakeHTLC allows a user to receive the Algo prior to a deadline (in terms of a round) by proving a knowledge
    21  // of a special value or to forfeit the ability to claim, returning it to the payer.
    22  // This contract is usually used to perform cross-chained atomic swaps
    23  //
    24  // More formally -
    25  // Algos can be transferred under only two circumstances:
    26  // 1. To receiver if hash_function(arg_0) = hash_value
    27  // 2. To owner if txn.FirstValid > expiry_round
    28  // ...
    29  //
    30  // Parameters:
    31  // - owner : string an address that can receive the asset after the expiry round
    32  // - receiver: string address to receive Algos
    33  // - hashFunction : string the hash function to be used (must be either sha256 or keccak256)
    34  // - hashImage : string the hash image in base64
    35  // - expiryRound : uint64 the round on which the assets can be transferred back to owner
    36  // - maxFee : uint64 the maximum fee that can be paid to the network by the account
    37  //
    38  // Deprecated: Use TealCompile source compilation instead.
    39  func MakeHTLC(owner, receiver, hashFunction, hashImage string, expiryRound, maxFee uint64) (HTLC, error) {
    40  	var referenceProgram string
    41  	if hashFunction == "sha256" {
    42  		referenceProgram = "ASAECAEACSYDIOaalh5vLV96yGYHkmVSvpgjXtMzY8qIkYu5yTipFbb5IH+DsWV/8fxTuS3BgUih1l38LUsfo9Z3KErd0gASbZBpIP68oLsUSlpOp7Q4pGgayA5soQW8tgf8VlMlyVaV9qITMQEiDjEQIxIQMQcyAxIQMQgkEhAxCSgSLQEpEhAxCSoSMQIlDRAREA=="
    43  	} else if hashFunction == "keccak256" {
    44  		referenceProgram = "ASAECAEACSYDIOaalh5vLV96yGYHkmVSvpgjXtMzY8qIkYu5yTipFbb5IH+DsWV/8fxTuS3BgUih1l38LUsfo9Z3KErd0gASbZBpIP68oLsUSlpOp7Q4pGgayA5soQW8tgf8VlMlyVaV9qITMQEiDjEQIxIQMQcyAxIQMQgkEhAxCSgSLQIpEhAxCSoSMQIlDRAREA=="
    45  	} else {
    46  		return HTLC{}, fmt.Errorf("invalid hash function supplied")
    47  	}
    48  	referenceAsBytes, err := base64.StdEncoding.DecodeString(referenceProgram)
    49  	if err != nil {
    50  		return HTLC{}, err
    51  	}
    52  	ownerAddr, err := types.DecodeAddress(owner)
    53  	if err != nil {
    54  		return HTLC{}, err
    55  	}
    56  	receiverAddr, err := types.DecodeAddress(receiver)
    57  	if err != nil {
    58  		return HTLC{}, err
    59  	}
    60  	//validate hashImage
    61  	_, err = base64.StdEncoding.DecodeString(hashImage)
    62  	if err != nil {
    63  		return HTLC{}, err
    64  	}
    65  	var referenceOffsets = []uint64{ /*fee*/ 3 /*expiryRound*/, 6 /*receiver*/, 10 /*hashImage*/, 42 /*owner*/, 76}
    66  	injectionVector := []interface{}{maxFee, expiryRound, receiverAddr, hashImage, ownerAddr}
    67  	injectedBytes, err := inject(referenceAsBytes, referenceOffsets, injectionVector)
    68  	if err != nil {
    69  		return HTLC{}, err
    70  	}
    71  
    72  	address := crypto.AddressFromProgram(injectedBytes)
    73  	htlc := HTLC{
    74  		ContractTemplate: ContractTemplate{
    75  			address: address.String(),
    76  			program: injectedBytes,
    77  		},
    78  	}
    79  	return htlc, err
    80  }
    81  
    82  // SignTransactionWithHTLCUnlock accepts a transaction, such as a payment, and builds the HTLC-unlocking signature around that transaction
    83  //
    84  // Deprecated: Use TealCompile source compilation instead.
    85  func SignTransactionWithHTLCUnlock(program []byte, txn types.Transaction, preImageAsBase64 string) (txid string, stx []byte, err error) {
    86  	preImageAsArgument, err := base64.StdEncoding.DecodeString(preImageAsBase64)
    87  	if err != nil {
    88  		return
    89  	}
    90  	hashFunction := program[len(program)-15]
    91  	_, byteArrays, err := logic.ReadProgram(program, nil)
    92  	expectedHashImage := byteArrays[1]
    93  	if err != nil {
    94  		return
    95  	}
    96  	if hashFunction == 1 {
    97  		sha256 := sha256.Sum256(preImageAsArgument)
    98  		if !bytes.Equal(sha256[:], expectedHashImage) {
    99  			err = fmt.Errorf("sha256 hash of preimage failed to match expected hash image")
   100  			return
   101  		}
   102  	} else if hashFunction == 2 {
   103  		keccak256 := sha3.NewLegacyKeccak256()
   104  		keccak256Hash := keccak256.Sum(preImageAsArgument)
   105  		if !bytes.Equal(keccak256Hash[:], expectedHashImage) {
   106  			err = fmt.Errorf("keccak256 hash of preimage failed to match expected hash image")
   107  		}
   108  	} else {
   109  		err = fmt.Errorf("found invalid hash function %d in contract", hashFunction)
   110  		return
   111  	}
   112  	args := make([][]byte, 1)
   113  	args[0] = preImageAsArgument
   114  	var blankMultisig crypto.MultisigAccount
   115  	lsig, err := crypto.MakeLogicSig(program, args, nil, blankMultisig)
   116  	if err != nil {
   117  		return
   118  	}
   119  	txn.Receiver = types.Address{} //txn must have no receiver but MakePayment et al disallow this.
   120  	txid, stx, err = crypto.SignLogicsigTransaction(lsig, txn)
   121  	return
   122  }