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 }