From 26eff5c93fee3b0a2e5b84afbb271de5e532bf5c Mon Sep 17 00:00:00 2001 From: Jason Paulos Date: Fri, 9 Jul 2021 08:47:25 -0700 Subject: [PATCH 1/6] Use correct go version in CI (#228) * Use current Go version in Dockerfile * Add image printout * Use actual variable --- .travis.yml | 2 ++ test/docker/Dockerfile | 3 ++- test/docker/run_docker.sh | 7 ++++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index dd814517..b03bb640 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,8 @@ go: - "1.12" - "1.13" - "1.14" + - "1.15" + - "1.16" install: - go get -u golang.org/x/lint/golint script: diff --git a/test/docker/Dockerfile b/test/docker/Dockerfile index f4d419a4..e6e1efb6 100644 --- a/test/docker/Dockerfile +++ b/test/docker/Dockerfile @@ -1,4 +1,5 @@ -FROM golang:1.13-stretch +ARG GO_IMAGE=golang:1.13-stretch +FROM $GO_IMAGE # Copy SDK code into the container RUN mkdir -p $HOME/go-algorand-sdk diff --git a/test/docker/run_docker.sh b/test/docker/run_docker.sh index 280a13a2..462a57fd 100755 --- a/test/docker/run_docker.sh +++ b/test/docker/run_docker.sh @@ -9,8 +9,13 @@ git clone --single-branch --branch master https://github.com/algorand/algorand-s #copy feature files into project mv test-harness/features test/features +GO_VERSION=$(go version | cut -d' ' -f 3 | cut -d'.' -f 1,2) +GO_IMAGE=golang:${GO_VERSION:2}-stretch + +echo "Building docker image from base \"$GO_IMAGE\"" + #build test environment -docker build -t go-sdk-testing -f test/docker/Dockerfile "$(pwd)" +docker build -t go-sdk-testing --build-arg GO_IMAGE="$GO_IMAGE" -f test/docker/Dockerfile "$(pwd)" # Start test harness environment ./test-harness/scripts/up.sh -p From 32780c56a1da33a0d9b2f51b66c5756d674ece49 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Wed, 21 Jul 2021 07:29:49 -0400 Subject: [PATCH 2/6] Add Asset Base64 Fields (#233) --- Makefile | 8 ++++++++ client/v2/common/models/asset_params.go | 21 ++++++++++++++++++--- types/basics_test.go | 2 +- types/transaction.go | 2 +- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 3e21532b..388e6000 100644 --- a/Makefile +++ b/Makefile @@ -5,12 +5,18 @@ TEST_SOURCES_NO_CUCUMBER := $(shell cd $(SRCPATH) && go list ./... | grep -v tes lint: golint `go list ./... | grep -v /vendor/` +fmt: + go fmt ./... + generate: cd $(SRCPATH) && go generate ./logic build: generate cd $(SRCPATH) && go test -run xxx_phony_test $(TEST_SOURCES) +test: + go test $(TEST_SOURCES_NO_CUCUMBER) + unit: go test $(TEST_SOURCES_NO_CUCUMBER) cd test && go test --godog.strict=true --godog.format=pretty --godog.tags="@unit.offline,@unit.algod,@unit.indexer,@unit.rekey,@unit.tealsign,@unit.dryrun,@unit.responses,@unit.applications,@unit.transactions,@unit.indexer.rekey,@unit.responses.messagepack,@unit.responses.231,@unit.responses.messagepack.231,@unit.responses.genesis,@unit.feetest" --test.v . @@ -21,3 +27,5 @@ integration: docker-test: ./test/docker/run_docker.sh + +.PHONY: test fmt diff --git a/client/v2/common/models/asset_params.go b/client/v2/common/models/asset_params.go index 905a06ed..6dbf7a0a 100644 --- a/client/v2/common/models/asset_params.go +++ b/client/v2/common/models/asset_params.go @@ -35,18 +35,33 @@ type AssetParams struct { // this metadata is up to the application. MetadataHash []byte `json:"metadata-hash,omitempty"` - // Name (an) Name of this asset, as supplied by the creator. + // Name (an) Name of this asset, as supplied by the creator. Included only when the + // asset name is composed of printable utf-8 characters. Name string `json:"name,omitempty"` + // NameB64 base64 encoded name of this asset, as supplied by the creator. + NameB64 []byte `json:"name-b64,omitempty"` + // Reserve (r) Address of account holding reserve (non-minted) units of this asset. Reserve string `json:"reserve,omitempty"` // Total (t) The total number of units of this asset. Total uint64 `json:"total"` - // UnitName (un) Name of a unit of this asset, as supplied by the creator. + // UnitName (un) Name of a unit of this asset, as supplied by the creator. Included + // only when the name of a unit of this asset is composed of printable utf-8 + // characters. UnitName string `json:"unit-name,omitempty"` - // Url (au) URL where more information about the asset can be retrieved. + // UnitNameB64 base64 encoded name of a unit of this asset, as supplied by the + // creator. + UnitNameB64 []byte `json:"unit-name-b64,omitempty"` + + // Url (au) URL where more information about the asset can be retrieved. Included + // only when the URL is composed of printable utf-8 characters. Url string `json:"url,omitempty"` + + // UrlB64 base64 encoded URL where more information about the asset can be + // retrieved. + UrlB64 []byte `json:"url-b64,omitempty"` } diff --git a/types/basics_test.go b/types/basics_test.go index 16bc464a..364e98b5 100644 --- a/types/basics_test.go +++ b/types/basics_test.go @@ -37,4 +37,4 @@ func TestBlockFromBase64String(t *testing.T) { err := vbl.FromBase64String(b64data) require.NoError(t, err) require.Equal(t, vbl.CurrentProtocol, protocol) -} \ No newline at end of file +} diff --git a/types/transaction.go b/types/transaction.go index 372ca153..74d5c0fd 100644 --- a/types/transaction.go +++ b/types/transaction.go @@ -188,7 +188,7 @@ type SuggestedParams struct { // The minimum transaction fee (not per byte) required for the // txn to validate for the current network protocol. - MinFee uint64 `codec:"min-fee"` + MinFee uint64 `codec:"min-fee"` } // AddLease adds the passed lease (see types/transaction.go) to the header of the passed transaction From f7ebc68014d6daffa306a260c8a556ee43ef20bc Mon Sep 17 00:00:00 2001 From: algojack <87339414+algojack@users.noreply.github.com> Date: Wed, 21 Jul 2021 08:50:52 -0400 Subject: [PATCH 3/6] Create github feature request and bug report templates (#231) --- .github/ISSUE_TEMPLATE/bug_report.md | 30 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 23 +++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..88003160 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,30 @@ +--- +name: "\U0001F41C Bug report" +about: Report a reproducible bug. +title: '' +labels: new-bug +assignees: '' + +--- + +### Subject of the issue + + + +### Your environment + + + +### Steps to reproduce + +1. +2. + +### Expected behaviour + +### Actual behaviour diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..7a16b83c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,23 @@ +--- +name: "\U0001F514 Feature Request" +about: Suggestions for how we can improve the algorand platform. +title: '' +labels: new-feature-request +assignees: '' +--- + +## Problem + + + +## Solution + + + +## Dependencies + + + +## Urgency + + From e86176c14adddaa1747863e330da396fe9f31651 Mon Sep 17 00:00:00 2001 From: Jason Paulos Date: Wed, 21 Jul 2021 12:56:50 -0700 Subject: [PATCH 4/6] Signing support for rekeying to LogicSig/MultiSig account (#230) * Allow MultiSigs and LogicSigs to sign transactions from any address * Fix failing test * Allow signing of LogicSigs without SigKey if the sender is the delegating account for compatibility * Don't modify LogicSig, create LogicSigAccount struct instead * Update function description * Revert template test change * Add deprecation warning * Restrict AccountFromPrivateKey input to 64 byte private key * LogicSigAccount.SignTranasction -> SignLogicSigAccountTransaction * Fix merging multisig txns with auth addrs --- crypto/account.go | 207 +++++++++++++++++++- crypto/account_test.go | 327 ++++++++++++++++++++++++++++++- crypto/crypto.go | 155 +++++++++++---- crypto/crypto_test.go | 425 +++++++++++++++++++++++++++++++++++++++-- crypto/errors.go | 8 +- types/signature.go | 5 +- 6 files changed, 1070 insertions(+), 57 deletions(-) diff --git a/crypto/account.go b/crypto/account.go index 39d62742..9fb368d5 100644 --- a/crypto/account.go +++ b/crypto/account.go @@ -2,6 +2,7 @@ package crypto import ( "crypto/sha512" + "errors" "fmt" "golang.org/x/crypto/ed25519" @@ -50,6 +51,30 @@ func GenerateAccount() (kp Account) { return } +// AccountFromPrivateKey derives the remaining Account fields from only a +// private key. The argument sk must have a length equal to +// ed25519.PrivateKeySize. +func AccountFromPrivateKey(sk ed25519.PrivateKey) (account Account, err error) { + if len(sk) != ed25519.PrivateKeySize { + err = errInvalidPrivateKey + return + } + + // copy sk + account.PrivateKey = make(ed25519.PrivateKey, len(sk)) + copy(account.PrivateKey, sk) + + account.PublicKey = sk.Public().(ed25519.PublicKey) + if len(account.PublicKey) != ed25519.PublicKeySize { + err = errors.New("generated public key is the wrong size") + return + } + + copy(account.Address[:], account.PublicKey) + + return +} + /* Multisig Support */ // MultisigAccount is a convenience type for holding multisig preimage data @@ -133,7 +158,12 @@ func (ma MultisigAccount) Blank() bool { return true } -// LogicSigAddress returns contract (escrow) address +/* LogicSig support */ + +// LogicSigAddress returns the contract (escrow) address for a LogicSig. +// +// NOTE: If the LogicSig is delegated to another account this will not +// return the delegated address of the LogicSig. func LogicSigAddress(lsig types.LogicSig) types.Address { toBeSigned := programToSign(lsig.Logic) checksum := sha512.Sum512_256(toBeSigned) @@ -145,3 +175,178 @@ func LogicSigAddress(lsig types.LogicSig) types.Address { } return addr } + +// LogicSigAccount represents an account that can sign with a LogicSig program. +type LogicSigAccount struct { + _struct struct{} `codec:",omitempty,omitemptyarray"` + + // The underlying LogicSig object + Lsig types.LogicSig `codec:"lsig"` + + // The key that provided Lsig.Sig, if any + SigningKey ed25519.PublicKey `codec:"sigkey"` +} + +// MakeLogicSigAccountEscrow creates a new escrow LogicSigAccount. The address +// of this account will be a hash of its program. +func MakeLogicSigAccountEscrow(program []byte, args [][]byte) LogicSigAccount { + return LogicSigAccount{ + Lsig: types.LogicSig{ + Logic: program, + Args: args, + }, + } +} + +// MakeLogicSigAccountDelegated creates a new delegated LogicSigAccount. This +// type of LogicSig has the authority to sign transactions on behalf of another +// account, called the delegating account. If the delegating account is a +// multisig account, use MakeLogicSigAccountDelegated instead. +// +// The parameter signer is the private key of the delegating account. +func MakeLogicSigAccountDelegated(program []byte, args [][]byte, signer ed25519.PrivateKey) (lsa LogicSigAccount, err error) { + var ma MultisigAccount + lsig, err := MakeLogicSig(program, args, signer, ma) + if err != nil { + return + } + + signerAccount, err := AccountFromPrivateKey(signer) + if err != nil { + return + } + + lsa = LogicSigAccount{ + Lsig: lsig, + // attach SigningKey to remember which account the signature belongs to + SigningKey: signerAccount.PublicKey, + } + return +} + +// MakeLogicSigAccountDelegatedMsig creates a new delegated LogicSigAccount. +// This type of LogicSig has the authority to sign transactions on behalf of +// another account, called the delegating account. Use this function if the +// delegating account is a multisig account, otherwise use +// MakeLogicSigAccountDelegated. +// +// The parameter msigAccount is the delegating multisig account. +// +// The parameter signer is the private key of one of the members of the +// delegating multisig account. Use the method AppendMultisigSignature on the +// returned LogicSigAccount to add additional signatures from other members. +func MakeLogicSigAccountDelegatedMsig(program []byte, args [][]byte, msigAccount MultisigAccount, signer ed25519.PrivateKey) (lsa LogicSigAccount, err error) { + lsig, err := MakeLogicSig(program, args, signer, msigAccount) + if err != nil { + return + } + lsa = LogicSigAccount{ + Lsig: lsig, + // do not attach SigningKey, since that doesn't apply to an msig signature + } + return +} + +// AppendMultisigSignature adds an additional signature from a member of the +// delegating multisig account. +// +// The LogicSigAccount must represent a delegated LogicSig backed by a multisig +// account. +func (lsa *LogicSigAccount) AppendMultisigSignature(signer ed25519.PrivateKey) error { + return AppendMultisigToLogicSig(&lsa.Lsig, signer) +} + +// LogicSigAccountFromLogicSig creates a LogicSigAccount from an existing +// LogicSig object. +// +// The parameter signerPublicKey must be present if the LogicSig is delegated +// and the delegating account is backed by a single private key (i.e. not a +// multisig account). In this case, signerPublicKey must be the public key of +// the delegating account. In all other cases, an error will be returned if +// signerPublicKey is present. +func LogicSigAccountFromLogicSig(lsig types.LogicSig, signerPublicKey *ed25519.PublicKey) (lsa LogicSigAccount, err error) { + hasSig := lsig.Sig != (types.Signature{}) + hasMsig := !lsig.Msig.Blank() + + if hasSig && hasMsig { + err = errLsigTooManySignatures + return + } + + if hasSig { + if signerPublicKey == nil { + err = errLsigNoPublicKey + return + } + + toBeSigned := programToSign(lsig.Logic) + valid := ed25519.Verify(*signerPublicKey, toBeSigned, lsig.Sig[:]) + if !valid { + err = errLsigInvalidPublicKey + return + } + + lsa.Lsig = lsig + lsa.SigningKey = make(ed25519.PublicKey, len(*signerPublicKey)) + copy(lsa.SigningKey, *signerPublicKey) + return + } + + if signerPublicKey != nil { + err = errLsigAccountPublicKeyNotNeeded + return + } + + lsa.Lsig = lsig + return +} + +// IsDelegated returns true if and only if the LogicSig has been delegated to +// another account with a signature. +// +// Note this function only checks for the presence of a delegation signature. To +// verify the delegation signature, use VerifyLogicSig. +func (lsa LogicSigAccount) IsDelegated() bool { + hasSig := lsa.Lsig.Sig != (types.Signature{}) + hasMsig := !lsa.Lsig.Msig.Blank() + return hasSig || hasMsig +} + +// Address returns the address of this LogicSigAccount. +// +// If the LogicSig is delegated to another account, this will return the address +// of that account. +// +// If the LogicSig is not delegated to another account, this will return an +// escrow address that is the hash of the LogicSig's program code. +func (lsa LogicSigAccount) Address() (addr types.Address, err error) { + hasSig := lsa.Lsig.Sig != (types.Signature{}) + hasMsig := !lsa.Lsig.Msig.Blank() + + // require at most one sig + if hasSig && hasMsig { + err = errLsigTooManySignatures + return + } + + if hasSig { + n := copy(addr[:], lsa.SigningKey) + if n != ed25519.PublicKeySize { + err = fmt.Errorf("Generated public key has length of %d, expected %d", n, ed25519.PublicKeySize) + } + return + } + + if hasMsig { + var msigAccount MultisigAccount + msigAccount, err = MultisigAccountFromSig(lsa.Lsig.Msig) + if err != nil { + return + } + addr, err = msigAccount.Address() + return + } + + addr = LogicSigAddress(lsa.Lsig) + return +} diff --git a/crypto/account_test.go b/crypto/account_test.go index 02ee8da2..cb407df1 100644 --- a/crypto/account_test.go +++ b/crypto/account_test.go @@ -6,21 +6,69 @@ import ( "github.com/stretchr/testify/require" "golang.org/x/crypto/ed25519" + "github.com/algorand/go-algorand-sdk/mnemonic" "github.com/algorand/go-algorand-sdk/types" ) -func TestKeyGeneration(t *testing.T) { +func TestGenerateAccount(t *testing.T) { kp := GenerateAccount() // Public key should not be empty - require.NotEqual(t, kp.PublicKey, ed25519.PublicKey{}) + require.NotEqual(t, ed25519.PublicKey{}, kp.PublicKey) - // Public key should not be empty - require.NotEqual(t, kp.PrivateKey, ed25519.PrivateKey{}) + // Private key should not be empty + require.NotEqual(t, ed25519.PrivateKey{}, kp.PrivateKey) + + // Account should equal itself + require.Equal(t, kp, kp) // Address should be identical to public key pk := ed25519.PublicKey(kp.Address[:]) require.Equal(t, pk, kp.PublicKey) + + message := []byte("test message") + sig := ed25519.Sign(kp.PrivateKey, message) + // Public key should verify signature from private key + require.True(t, ed25519.Verify(kp.PublicKey, message, sig)) + + kp2 := GenerateAccount() + // Calling the function again should produce a different account + require.NotEqual(t, kp, kp2) +} + +func TestAccountFromPrivateKey(t *testing.T) { + exampleAccount := Account{ + PrivateKey: ed25519.PrivateKey{0xd2, 0xdc, 0x4c, 0xcc, 0xe9, 0x98, 0x62, 0xff, 0xcf, 0x8c, 0xeb, 0x93, 0x6, 0xc4, 0x8d, 0xa6, 0x80, 0x50, 0x82, 0xa, 0xbb, 0x29, 0x95, 0x7a, 0xac, 0x82, 0x68, 0x9a, 0x8c, 0x49, 0x5a, 0x38, 0x5e, 0x67, 0x4f, 0x1c, 0xa, 0xee, 0xec, 0x37, 0x71, 0x89, 0x8f, 0x61, 0xc7, 0x6f, 0xf5, 0xd2, 0x4a, 0x19, 0x79, 0x3e, 0x2c, 0x91, 0xfa, 0x8, 0x51, 0x62, 0x63, 0xe3, 0x85, 0x73, 0xea, 0x42}, + PublicKey: ed25519.PublicKey{0x5e, 0x67, 0x4f, 0x1c, 0xa, 0xee, 0xec, 0x37, 0x71, 0x89, 0x8f, 0x61, 0xc7, 0x6f, 0xf5, 0xd2, 0x4a, 0x19, 0x79, 0x3e, 0x2c, 0x91, 0xfa, 0x8, 0x51, 0x62, 0x63, 0xe3, 0x85, 0x73, 0xea, 0x42}, + Address: types.Address{0x5e, 0x67, 0x4f, 0x1c, 0xa, 0xee, 0xec, 0x37, 0x71, 0x89, 0x8f, 0x61, 0xc7, 0x6f, 0xf5, 0xd2, 0x4a, 0x19, 0x79, 0x3e, 0x2c, 0x91, 0xfa, 0x8, 0x51, 0x62, 0x63, 0xe3, 0x85, 0x73, 0xea, 0x42}, + } + + t.Run("From private key", func(t *testing.T) { + pk := exampleAccount.PrivateKey[:] + + actual, err := AccountFromPrivateKey(pk) + require.NoError(t, err) + + require.Equal(t, exampleAccount, actual) + }) + + t.Run("From seed only", func(t *testing.T) { + pk := exampleAccount.PrivateKey.Seed() // get just the seed portion of the private key (first 32 bytes) + + _, err := AccountFromPrivateKey(pk) + require.Error(t, err, errInvalidPrivateKey) + }) + + t.Run("From mnemonic", func(t *testing.T) { + m := "olympic cricket tower model share zone grid twist sponsor avoid eight apology patient party success claim famous rapid donor pledge bomb mystery security ability often" + pk, err := mnemonic.ToPrivateKey(m) + require.NoError(t, err) + + actual, err := AccountFromPrivateKey(pk) + require.NoError(t, err) + + require.Equal(t, exampleAccount, actual) + }) } func TestMultisigAccount_Address(t *testing.T) { @@ -66,3 +114,274 @@ func TestMultisigAccount_Version1Only(t *testing.T) { }) require.Error(t, ma.Validate()) } + +func TestLogicSigAddress(t *testing.T) { + program := []byte{1, 32, 1, 1, 34} + var args [][]byte + + expectedAddr, err := types.DecodeAddress("6Z3C3LDVWGMX23BMSYMANACQOSINPFIRF77H7N3AWJZYV6OH6GWTJKVMXY") + require.NoError(t, err) + + t.Run("no sig", func(t *testing.T) { + var sk ed25519.PrivateKey + var ma MultisigAccount + + lsig, err := MakeLogicSig(program, args, sk, ma) + require.NoError(t, err) + + actualAddr := LogicSigAddress(lsig) + require.Equal(t, expectedAddr, actualAddr) + }) + + t.Run("single sig", func(t *testing.T) { + account, err := AccountFromPrivateKey(ed25519.PrivateKey{0xd2, 0xdc, 0x4c, 0xcc, 0xe9, 0x98, 0x62, 0xff, 0xcf, 0x8c, 0xeb, 0x93, 0x6, 0xc4, 0x8d, 0xa6, 0x80, 0x50, 0x82, 0xa, 0xbb, 0x29, 0x95, 0x7a, 0xac, 0x82, 0x68, 0x9a, 0x8c, 0x49, 0x5a, 0x38, 0x5e, 0x67, 0x4f, 0x1c, 0xa, 0xee, 0xec, 0x37, 0x71, 0x89, 0x8f, 0x61, 0xc7, 0x6f, 0xf5, 0xd2, 0x4a, 0x19, 0x79, 0x3e, 0x2c, 0x91, 0xfa, 0x8, 0x51, 0x62, 0x63, 0xe3, 0x85, 0x73, 0xea, 0x42}) + require.NoError(t, err) + + var ma MultisigAccount + + lsig, err := MakeLogicSig(program, args, account.PrivateKey, ma) + require.NoError(t, err) + + // for backwards compatibility, we still expect the hashed program bytes address + actualAddr := LogicSigAddress(lsig) + require.Equal(t, expectedAddr, actualAddr) + }) + + t.Run("multi sig", func(t *testing.T) { + ma, sk1, _, _ := makeTestMultisigAccount(t) + + lsig, err := MakeLogicSig(program, args, sk1, ma) + require.NoError(t, err) + + // for backwards compatibility, we still expect the hashed program bytes address + actualAddr := LogicSigAddress(lsig) + require.Equal(t, expectedAddr, actualAddr) + }) +} + +func TestMakeLogicSigAccount(t *testing.T) { + program := []byte{1, 32, 1, 1, 34} + args := [][]byte{ + {0x01}, + {0x02, 0x03}, + } + + t.Run("Escrow", func(t *testing.T) { + lsigAccount := MakeLogicSigAccountEscrow(program, args) + + require.Equal(t, program, lsigAccount.Lsig.Logic) + require.Equal(t, args, lsigAccount.Lsig.Args) + require.Equal(t, types.Signature{}, lsigAccount.Lsig.Sig) + require.True(t, lsigAccount.Lsig.Msig.Blank()) + require.Equal(t, ed25519.PublicKey(nil), lsigAccount.SigningKey) + + require.False(t, lsigAccount.IsDelegated()) + }) + + t.Run("Delegated", func(t *testing.T) { + account, err := AccountFromPrivateKey(ed25519.PrivateKey{0xd2, 0xdc, 0x4c, 0xcc, 0xe9, 0x98, 0x62, 0xff, 0xcf, 0x8c, 0xeb, 0x93, 0x6, 0xc4, 0x8d, 0xa6, 0x80, 0x50, 0x82, 0xa, 0xbb, 0x29, 0x95, 0x7a, 0xac, 0x82, 0x68, 0x9a, 0x8c, 0x49, 0x5a, 0x38, 0x5e, 0x67, 0x4f, 0x1c, 0xa, 0xee, 0xec, 0x37, 0x71, 0x89, 0x8f, 0x61, 0xc7, 0x6f, 0xf5, 0xd2, 0x4a, 0x19, 0x79, 0x3e, 0x2c, 0x91, 0xfa, 0x8, 0x51, 0x62, 0x63, 0xe3, 0x85, 0x73, 0xea, 0x42}) + require.NoError(t, err) + + lsigAccount, err := MakeLogicSigAccountDelegated(program, args, account.PrivateKey) + require.NoError(t, err) + + expectedSignature := types.Signature{0x3e, 0x5, 0x3d, 0x39, 0x4d, 0xfb, 0x12, 0xbc, 0x65, 0x79, 0x9f, 0xea, 0x31, 0x8a, 0x7b, 0x8e, 0xa2, 0x51, 0x8b, 0x55, 0x2c, 0x8a, 0xbe, 0x6c, 0xd7, 0xa7, 0x65, 0x2d, 0xd8, 0xb0, 0x18, 0x7e, 0x21, 0x5, 0x2d, 0xb9, 0x24, 0x62, 0x89, 0x16, 0xe5, 0x61, 0x74, 0xcd, 0xf, 0x19, 0xac, 0xb9, 0x6c, 0x45, 0xa4, 0x29, 0x91, 0x99, 0x11, 0x1d, 0xe4, 0x7c, 0xe4, 0xfc, 0x12, 0xec, 0xce, 0x2} + + require.Equal(t, program, lsigAccount.Lsig.Logic) + require.Equal(t, args, lsigAccount.Lsig.Args) + require.Equal(t, expectedSignature, lsigAccount.Lsig.Sig) + require.True(t, lsigAccount.Lsig.Msig.Blank()) + require.Equal(t, account.PublicKey, lsigAccount.SigningKey) + + require.True(t, lsigAccount.IsDelegated()) + }) + + t.Run("DelegatedMsig", func(t *testing.T) { + ma, sk1, sk2, _ := makeTestMultisigAccount(t) + + lsigAccount, err := MakeLogicSigAccountDelegatedMsig(program, args, ma, sk1) + require.NoError(t, err) + + expectedMsig := types.MultisigSig{ + Version: ma.Version, + Threshold: ma.Threshold, + Subsigs: []types.MultisigSubsig{ + { + Key: ma.Pks[0], + Sig: types.Signature{0x49, 0x13, 0xb8, 0x5, 0xd1, 0x9e, 0x7f, 0x2c, 0x10, 0x80, 0xf6, 0x33, 0x7e, 0x18, 0x54, 0xa7, 0xce, 0xea, 0xee, 0x10, 0xdd, 0xbd, 0x13, 0x65, 0x84, 0xbf, 0x93, 0xb7, 0x5f, 0x30, 0x63, 0x15, 0x91, 0xca, 0x23, 0xc, 0xed, 0xef, 0x23, 0xd1, 0x74, 0x1b, 0x52, 0x9d, 0xb0, 0xff, 0xef, 0x37, 0x54, 0xd6, 0x46, 0xf4, 0xb5, 0x61, 0xfc, 0x8b, 0xbc, 0x2d, 0x7b, 0x4e, 0x63, 0x5c, 0xbd, 0x2}, + }, + { + Key: ma.Pks[1], + }, + { + Key: ma.Pks[2], + }, + }, + } + + require.Equal(t, program, lsigAccount.Lsig.Logic) + require.Equal(t, args, lsigAccount.Lsig.Args) + require.Equal(t, types.Signature{}, lsigAccount.Lsig.Sig) + require.Equal(t, expectedMsig, lsigAccount.Lsig.Msig) + require.Equal(t, ed25519.PublicKey(nil), lsigAccount.SigningKey) + + require.True(t, lsigAccount.IsDelegated()) + + t.Run("AppendMultisigSignature", func(t *testing.T) { + err := lsigAccount.AppendMultisigSignature(sk2) + require.NoError(t, err) + + expectedMsig.Subsigs[1].Sig = types.Signature{0x64, 0xbc, 0x55, 0xdb, 0xed, 0x91, 0xa2, 0x41, 0xd4, 0x2a, 0xb6, 0x60, 0xf7, 0xe1, 0x4a, 0xb9, 0x99, 0x9a, 0x52, 0xb3, 0xb1, 0x71, 0x58, 0xce, 0xfc, 0x3f, 0x4f, 0xe7, 0xcb, 0x22, 0x41, 0x14, 0xad, 0xa9, 0x3d, 0x5e, 0x84, 0x5, 0x2, 0xa, 0x17, 0xa6, 0x69, 0x83, 0x3, 0x22, 0x4e, 0x86, 0xa3, 0x8b, 0x6a, 0x36, 0xc5, 0x54, 0xbe, 0x20, 0x50, 0xff, 0xd3, 0xee, 0xa8, 0xb3, 0x4, 0x9} + + require.Equal(t, program, lsigAccount.Lsig.Logic) + require.Equal(t, args, lsigAccount.Lsig.Args) + require.Equal(t, types.Signature{}, lsigAccount.Lsig.Sig) + require.Equal(t, expectedMsig, lsigAccount.Lsig.Msig) + require.Equal(t, ed25519.PublicKey(nil), lsigAccount.SigningKey) + + require.True(t, lsigAccount.IsDelegated()) + }) + }) +} + +func TestLogicSigAccountFromLogicSig(t *testing.T) { + program := []byte{1, 32, 1, 1, 34} + args := [][]byte{ + {0x01}, + {0x02, 0x03}, + } + + programAddr, err := types.DecodeAddress("6Z3C3LDVWGMX23BMSYMANACQOSINPFIRF77H7N3AWJZYV6OH6GWTJKVMXY") + require.NoError(t, err) + + programPublicKey := make(ed25519.PublicKey, len(programAddr)) + copy(programPublicKey, programAddr[:]) + + t.Run("no sig", func(t *testing.T) { + var sk ed25519.PrivateKey + var ma MultisigAccount + + lsig, err := MakeLogicSig(program, args, sk, ma) + require.NoError(t, err) + + t.Run("with public key", func(t *testing.T) { + _, err := LogicSigAccountFromLogicSig(lsig, &programPublicKey) + require.Error(t, err, errLsigAccountPublicKeyNotNeeded) + }) + + t.Run("without public key", func(t *testing.T) { + lsigAccount, err := LogicSigAccountFromLogicSig(lsig, nil) + require.NoError(t, err) + + require.Equal(t, lsig, lsigAccount.Lsig) + require.Equal(t, ed25519.PublicKey(nil), lsigAccount.SigningKey) + + require.False(t, lsigAccount.IsDelegated()) + }) + }) + + t.Run("single sig", func(t *testing.T) { + account, err := AccountFromPrivateKey(ed25519.PrivateKey{0xd2, 0xdc, 0x4c, 0xcc, 0xe9, 0x98, 0x62, 0xff, 0xcf, 0x8c, 0xeb, 0x93, 0x6, 0xc4, 0x8d, 0xa6, 0x80, 0x50, 0x82, 0xa, 0xbb, 0x29, 0x95, 0x7a, 0xac, 0x82, 0x68, 0x9a, 0x8c, 0x49, 0x5a, 0x38, 0x5e, 0x67, 0x4f, 0x1c, 0xa, 0xee, 0xec, 0x37, 0x71, 0x89, 0x8f, 0x61, 0xc7, 0x6f, 0xf5, 0xd2, 0x4a, 0x19, 0x79, 0x3e, 0x2c, 0x91, 0xfa, 0x8, 0x51, 0x62, 0x63, 0xe3, 0x85, 0x73, 0xea, 0x42}) + require.NoError(t, err) + + var ma MultisigAccount + + lsig, err := MakeLogicSig(program, args, account.PrivateKey, ma) + require.NoError(t, err) + + t.Run("with correct public key", func(t *testing.T) { + lsigAccount, err := LogicSigAccountFromLogicSig(lsig, &account.PublicKey) + require.NoError(t, err) + + require.Equal(t, lsig, lsigAccount.Lsig) + require.Equal(t, account.PublicKey, lsigAccount.SigningKey) + + require.True(t, lsigAccount.IsDelegated()) + }) + + t.Run("with incorrect public key", func(t *testing.T) { + var wrongPublicKey = make(ed25519.PublicKey, len(account.PublicKey)) + copy(wrongPublicKey, account.PublicKey) + wrongPublicKey[0] = 0xff + _, err := LogicSigAccountFromLogicSig(lsig, &wrongPublicKey) + require.Error(t, err, errLsigInvalidPublicKey) + }) + + t.Run("without public key", func(t *testing.T) { + _, err := LogicSigAccountFromLogicSig(lsig, nil) + require.Error(t, err, errLsigNoPublicKey) + }) + }) + + t.Run("multi sig", func(t *testing.T) { + ma, sk1, _, _ := makeTestMultisigAccount(t) + + lsig, err := MakeLogicSig(program, args, sk1, ma) + require.NoError(t, err) + + t.Run("with public key", func(t *testing.T) { + msigAddr, err := ma.Address() + require.NoError(t, err) + + msigPublicKey := make(ed25519.PublicKey, len(msigAddr)) + copy(msigPublicKey, msigAddr[:]) + + _, err = LogicSigAccountFromLogicSig(lsig, &msigPublicKey) + require.Error(t, err, errLsigAccountPublicKeyNotNeeded) + }) + + t.Run("without public key", func(t *testing.T) { + lsigAccount, err := LogicSigAccountFromLogicSig(lsig, nil) + require.NoError(t, err) + + require.Equal(t, lsig, lsigAccount.Lsig) + require.Equal(t, ed25519.PublicKey(nil), lsigAccount.SigningKey) + + require.True(t, lsigAccount.IsDelegated()) + }) + }) +} + +func TestLogicSigAccount_Address(t *testing.T) { + program := []byte{1, 32, 1, 1, 34} + args := [][]byte{ + {0x01}, + {0x02, 0x03}, + } + + t.Run("no sig", func(t *testing.T) { + lsigAccount := MakeLogicSigAccountEscrow(program, args) + + expectedAddr, err := types.DecodeAddress("6Z3C3LDVWGMX23BMSYMANACQOSINPFIRF77H7N3AWJZYV6OH6GWTJKVMXY") + require.NoError(t, err) + + actualAddr, err := lsigAccount.Address() + require.NoError(t, err) + require.Equal(t, expectedAddr, actualAddr) + }) + + t.Run("single sig", func(t *testing.T) { + account, err := AccountFromPrivateKey(ed25519.PrivateKey{0xd2, 0xdc, 0x4c, 0xcc, 0xe9, 0x98, 0x62, 0xff, 0xcf, 0x8c, 0xeb, 0x93, 0x6, 0xc4, 0x8d, 0xa6, 0x80, 0x50, 0x82, 0xa, 0xbb, 0x29, 0x95, 0x7a, 0xac, 0x82, 0x68, 0x9a, 0x8c, 0x49, 0x5a, 0x38, 0x5e, 0x67, 0x4f, 0x1c, 0xa, 0xee, 0xec, 0x37, 0x71, 0x89, 0x8f, 0x61, 0xc7, 0x6f, 0xf5, 0xd2, 0x4a, 0x19, 0x79, 0x3e, 0x2c, 0x91, 0xfa, 0x8, 0x51, 0x62, 0x63, 0xe3, 0x85, 0x73, 0xea, 0x42}) + require.NoError(t, err) + + lsigAccount, err := MakeLogicSigAccountDelegated(program, args, account.PrivateKey) + require.NoError(t, err) + + actualAddr, err := lsigAccount.Address() + require.NoError(t, err) + require.Equal(t, account.Address, actualAddr) + }) + + t.Run("multi sig", func(t *testing.T) { + ma, sk1, _, _ := makeTestMultisigAccount(t) + maAddr, err := ma.Address() + require.NoError(t, err) + + lsigAccount, err := MakeLogicSigAccountDelegatedMsig(program, args, ma, sk1) + require.NoError(t, err) + + actualAddr, err := lsigAccount.Address() + require.NoError(t, err) + require.Equal(t, maAddr, actualAddr) + }) +} diff --git a/crypto/crypto.go b/crypto/crypto.go index 6380e65d..6065c644 100644 --- a/crypto/crypto.go +++ b/crypto/crypto.go @@ -236,15 +236,6 @@ func SignMultisigTransaction(sk ed25519.PrivateKey, ma MultisigAccount, tx types if err != nil { return } - // check that the address of txn matches the preimage - maAddress, err := ma.Address() - if err != nil { - return - } - if tx.Sender != maAddress { // array value comparison is fine - err = errMsigBadTxnSender - return - } // this signer signs a transaction and sets txid from the closure customSigner := func() (rawSig types.Signature, err error) { @@ -262,6 +253,16 @@ func SignMultisigTransaction(sk ed25519.PrivateKey, ma MultisigAccount, tx types Msig: sig, Txn: tx, } + + maAddress, err := ma.Address() + if err != nil { + return + } + + if stx.Txn.Sender != maAddress { + stx.AuthAddr = maAddress + } + stxBytes = msgpack.Encode(stx) return } @@ -276,6 +277,7 @@ func MergeMultisigTransactions(stxsBytes ...[]byte) (txid string, stxBytes []byt var sig types.MultisigSig var refAddr *types.Address var refTx types.Transaction + var refAuthAddr types.Address for _, partStxBytes := range stxsBytes { partStx := types.SignedTxn{} err = msgpack.Decode(partStxBytes, &partStx) @@ -305,12 +307,19 @@ func MergeMultisigTransactions(stxsBytes ...[]byte) (txid string, stxBytes []byt sig.Subsigs[i].Key = c } refTx = partStx.Txn - } else { - if partAddr != *refAddr { - err = errMsigMergeKeysMismatch - return - } + refAuthAddr = partStx.AuthAddr + } + + if partAddr != *refAddr { + err = errMsigMergeKeysMismatch + return + } + + if partStx.AuthAddr != refAuthAddr { + err = errMsigMergeAuthAddrMismatch + return } + // now, add subsignatures appropriately zeroSig := types.Signature{} for i := 0; i < len(sig.Subsigs); i++ { @@ -327,8 +336,9 @@ func MergeMultisigTransactions(stxsBytes ...[]byte) (txid string, stxBytes []byt } // Encode the signedTxn stx := types.SignedTxn{ - Msig: sig, - Txn: refTx, + Msig: sig, + Txn: refTx, + AuthAddr: refAuthAddr, } stxBytes = msgpack.Encode(stx) // let's also compute the txid. @@ -355,6 +365,10 @@ func AppendMultisigTransaction(sk ed25519.PrivateKey, ma MultisigAccount, preStx } // VerifyMultisig verifies an assembled MultisigSig +// +// addr is the address of the Multisig account +// message is the bytes there were signed +// msig is the Multisig signature to verify func VerifyMultisig(addr types.Address, message []byte, msig types.MultisigSig) bool { msigAccount, err := MultisigAccountFromSig(msig) if err != nil { @@ -431,8 +445,14 @@ func ComputeGroupID(txgroup []types.Transaction) (gid types.Digest, err error) { /* LogicSig support */ -// VerifyLogicSig verifies LogicSig against assumed sender address -func VerifyLogicSig(lsig types.LogicSig, sender types.Address) (result bool) { +// VerifyLogicSig verifies that a LogicSig contains a valid program and, if a +// delegated signature is present, that the signature is valid. +// +// The singleSigner argument is only used in the case of a delegated LogicSig +// whose delegating account is backed by a single private key (i.e. not a +// multsig account). In that case, it should be the address of the delegating +// account. +func VerifyLogicSig(lsig types.LogicSig, singleSigner types.Address) (result bool) { if err := logic.CheckProgram(lsig.Logic, lsig.Args); err != nil { return false } @@ -445,30 +465,34 @@ func VerifyLogicSig(lsig types.LogicSig, sender types.Address) (result bool) { return false } - result = false toBeSigned := programToSign(lsig.Logic) - // logic sig, compare hashes - if !hasSig && !hasMsig { - result = types.Digest(sha512.Sum512_256(toBeSigned)) == types.Digest(sender) - return - } if hasSig { - result = ed25519.Verify(sender[:], toBeSigned, lsig.Sig[:]) - return + return ed25519.Verify(singleSigner[:], toBeSigned, lsig.Sig[:]) } - result = VerifyMultisig(sender, toBeSigned, lsig.Msig) - return + if hasMsig { + msigAccount, err := MultisigAccountFromSig(lsig.Msig) + if err != nil { + return false + } + addr, err := msigAccount.Address() + if err != nil { + return false + } + return VerifyMultisig(addr, toBeSigned, lsig.Msig) + } + + // the lsig account is the hash of its program bytes, nothing left to verify + return true } -// SignLogicsigTransaction takes LogicSig object and a transaction and returns the -// bytes of a signed transaction ready to be broadcasted to the network -// Note, LogicSig actually can be attached to any transaction (with matching sender field for Sig and Multisig cases) -// and it is a program's responsibility to approve/decline the transaction -func SignLogicsigTransaction(lsig types.LogicSig, tx types.Transaction) (txid string, stxBytes []byte, err error) { +// signLogicSigTransactionWithAddress signs a transaction with a LogicSig. +// +// lsigAddress is the address of the account that the LogicSig represents. +func signLogicSigTransactionWithAddress(lsig types.LogicSig, lsigAddress types.Address, tx types.Transaction) (txid string, stxBytes []byte, err error) { - if !VerifyLogicSig(lsig, tx.Header.Sender) { + if !VerifyLogicSig(lsig, lsigAddress) { err = errLsigInvalidSignature return } @@ -480,11 +504,69 @@ func SignLogicsigTransaction(lsig types.LogicSig, tx types.Transaction) (txid st Txn: tx, } + if stx.Txn.Sender != lsigAddress { + stx.AuthAddr = lsigAddress + } + // Encode the SignedTxn stxBytes = msgpack.Encode(stx) return } +// SignLogicSigAccountTransaction signs a transaction with a LogicSigAccount. It +// returns the TxID of the signed transaction and the raw bytes ready to be +// broadcast to the network. Note: any type of transaction can be signed by a +// LogicSig, but the network will reject the transaction if the LogicSig's +// program declines the transaction. +func SignLogicSigAccountTransaction(logicSigAccount LogicSigAccount, tx types.Transaction) (txid string, stxBytes []byte, err error) { + addr, err := logicSigAccount.Address() + if err != nil { + return + } + + txid, stxBytes, err = signLogicSigTransactionWithAddress(logicSigAccount.Lsig, addr, tx) + return +} + +// SignLogicsigTransaction takes LogicSig object and a transaction and returns the +// bytes of a signed transaction ready to be broadcasted to the network +// Note, LogicSig actually can be attached to any transaction and it is a +// program's responsibility to approve/decline the transaction +// +// This function supports signing transactions with a sender that differs from +// the LogicSig's address, EXCEPT IF the LogicSig is delegated to a non-multisig +// account. In order to properly handle that case, create a LogicSigAccount and +// use SignLogicSigAccountTransaction instead. +func SignLogicsigTransaction(lsig types.LogicSig, tx types.Transaction) (txid string, stxBytes []byte, err error) { + hasSig := lsig.Sig != (types.Signature{}) + hasMsig := !lsig.Msig.Blank() + + // the address that the LogicSig represents + var lsigAddress types.Address + if hasSig { + // For a LogicSig with a non-multisig delegating account, we cannot derive + // the address of that account from only its signature, so assume the + // delegating account is the sender. If that's not the case, the signing + // will fail. + lsigAddress = tx.Header.Sender + } else if hasMsig { + var msigAccount MultisigAccount + msigAccount, err = MultisigAccountFromSig(lsig.Msig) + if err != nil { + return + } + lsigAddress, err = msigAccount.Address() + if err != nil { + return + } + } else { + lsigAddress = LogicSigAddress(lsig) + } + + txid, stxBytes, err = signLogicSigTransactionWithAddress(lsig, lsigAddress, tx) + return +} + func programToSign(program []byte) []byte { parts := [][]byte{programPrefix, program} toBeSigned := bytes.Join(parts, nil) @@ -510,6 +592,11 @@ func AddressFromProgram(program []byte) types.Address { } // MakeLogicSig produces a new LogicSig signature. +// +// THIS FUNCTION IS DEPRECATED. It will be removed in v2 of this library. Use +// one of MakeLogicSigAccountEscrow, MakeLogicSigAccountDelegated, or +// MakeLogicSigAccountDelegatedMsig instead. +// // The function can work in three modes: // 1. If no sk and ma provided then it returns contract-only LogicSig // 2. If no ma provides, it returns Sig delegated LogicSig diff --git a/crypto/crypto_test.go b/crypto/crypto_test.go index 3271b250..29548897 100644 --- a/crypto/crypto_test.go +++ b/crypto/crypto_test.go @@ -70,12 +70,58 @@ func TestSignMultisigTransaction(t *testing.T) { var stx types.SignedTxn err = msgpack.Decode(txBytes, &stx) require.NoError(t, err) - bytesToSign := rawTransactionBytesToSign(stx.Txn) + require.Equal(t, types.Address{}, stx.AuthAddr) + require.Equal(t, tx, stx.Txn) + + bytesToSign := rawTransactionBytesToSign(stx.Txn) verified := VerifyMultisig(fromAddr, bytesToSign, stx.Msig) require.False(t, verified) // not enough signatures } +func TestSignMultisigTransactionWithAuthAddr(t *testing.T) { + ma, sk1, _, _ := makeTestMultisigAccount(t) + multisigAddr, err := ma.Address() + require.NoError(t, err) + fromAddr, err := types.DecodeAddress("47YPQTIGQEO7T4Y4RWDYWEKV6RTR2UNBQXBABEEGM72ESWDQNCQ52OPASU") + require.NoError(t, err) + toAddr, err := types.DecodeAddress("DN7MBMCL5JQ3PFUQS7TMX5AH4EEKOBJVDUF4TCV6WERATKFLQF4MQUPZTA") + require.NoError(t, err) + tx := types.Transaction{ + Type: types.PaymentTx, + Header: types.Header{ + Sender: fromAddr, + Fee: 217000, + FirstValid: 972508, + LastValid: 973508, + Note: []byte{180, 81, 121, 57, 252, 250, 210, 113}, + GenesisID: "testnet-v31.0", + }, + PaymentTxnFields: types.PaymentTxnFields{ + Receiver: toAddr, + Amount: 5000, + }, + } + txid, txBytes, err := SignMultisigTransaction(sk1, ma, tx) + require.NoError(t, err) + expectedBytes := []byte{0x83, 0xa4, 0x6d, 0x73, 0x69, 0x67, 0x83, 0xa6, 0x73, 0x75, 0x62, 0x73, 0x69, 0x67, 0x93, 0x82, 0xa2, 0x70, 0x6b, 0xc4, 0x20, 0x1b, 0x7e, 0xc0, 0xb0, 0x4b, 0xea, 0x61, 0xb7, 0x96, 0x90, 0x97, 0xe6, 0xcb, 0xf4, 0x7, 0xe1, 0x8, 0xa7, 0x5, 0x35, 0x1d, 0xb, 0xc9, 0x8a, 0xbe, 0xb1, 0x22, 0x9, 0xa8, 0xab, 0x81, 0x78, 0xa1, 0x73, 0xc4, 0x40, 0x3e, 0xd3, 0xf2, 0x25, 0x45, 0x61, 0x38, 0x4a, 0x72, 0xca, 0x59, 0x2b, 0xd7, 0xca, 0xfa, 0x7, 0xe, 0x1, 0xb1, 0xce, 0x3c, 0x2e, 0xd8, 0x8c, 0x70, 0x80, 0xa7, 0x9d, 0xe2, 0x88, 0xf7, 0xa2, 0xa3, 0xc9, 0xee, 0x88, 0x0, 0xb, 0x6c, 0x2c, 0x6a, 0x7, 0xca, 0x53, 0x22, 0xcb, 0xc, 0x78, 0x82, 0xcd, 0x42, 0xc9, 0x76, 0x6a, 0x72, 0xed, 0x3f, 0x7c, 0x8a, 0x47, 0x47, 0xaf, 0xb7, 0x0, 0x81, 0xa2, 0x70, 0x6b, 0xc4, 0x20, 0x9, 0x63, 0x32, 0x9, 0x53, 0x73, 0x89, 0xf0, 0x75, 0x67, 0x11, 0x77, 0x39, 0x91, 0xc7, 0xd0, 0x3e, 0x1b, 0x73, 0xc8, 0xc4, 0xf5, 0x2b, 0xf6, 0xaf, 0xf0, 0x1a, 0xa2, 0x5c, 0xf9, 0xc2, 0x71, 0x81, 0xa2, 0x70, 0x6b, 0xc4, 0x20, 0xe7, 0xf0, 0xf8, 0x4d, 0x6, 0x81, 0x1d, 0xf9, 0xf3, 0x1c, 0x8d, 0x87, 0x8b, 0x11, 0x55, 0xf4, 0x67, 0x1d, 0x51, 0xa1, 0x85, 0xc2, 0x0, 0x90, 0x86, 0x67, 0xf4, 0x49, 0x58, 0x70, 0x68, 0xa1, 0xa3, 0x74, 0x68, 0x72, 0x2, 0xa1, 0x76, 0x1, 0xa4, 0x73, 0x67, 0x6e, 0x72, 0xc4, 0x20, 0x8d, 0x92, 0xb4, 0x89, 0x90, 0x1, 0x73, 0xa0, 0x4d, 0xfa, 0x43, 0x59, 0xa3, 0x66, 0x6a, 0x6a, 0xfc, 0xea, 0x2c, 0x42, 0xa0, 0x5d, 0xd9, 0xc1, 0xf7, 0x3e, 0xeb, 0xa5, 0x47, 0x80, 0x37, 0xe9, 0xa3, 0x74, 0x78, 0x6e, 0x89, 0xa3, 0x61, 0x6d, 0x74, 0xcd, 0x13, 0x88, 0xa3, 0x66, 0x65, 0x65, 0xce, 0x0, 0x3, 0x4f, 0xa8, 0xa2, 0x66, 0x76, 0xce, 0x0, 0xe, 0xd6, 0xdc, 0xa3, 0x67, 0x65, 0x6e, 0xad, 0x74, 0x65, 0x73, 0x74, 0x6e, 0x65, 0x74, 0x2d, 0x76, 0x33, 0x31, 0x2e, 0x30, 0xa2, 0x6c, 0x76, 0xce, 0x0, 0xe, 0xda, 0xc4, 0xa4, 0x6e, 0x6f, 0x74, 0x65, 0xc4, 0x8, 0xb4, 0x51, 0x79, 0x39, 0xfc, 0xfa, 0xd2, 0x71, 0xa3, 0x72, 0x63, 0x76, 0xc4, 0x20, 0x1b, 0x7e, 0xc0, 0xb0, 0x4b, 0xea, 0x61, 0xb7, 0x96, 0x90, 0x97, 0xe6, 0xcb, 0xf4, 0x7, 0xe1, 0x8, 0xa7, 0x5, 0x35, 0x1d, 0xb, 0xc9, 0x8a, 0xbe, 0xb1, 0x22, 0x9, 0xa8, 0xab, 0x81, 0x78, 0xa3, 0x73, 0x6e, 0x64, 0xc4, 0x20, 0xe7, 0xf0, 0xf8, 0x4d, 0x6, 0x81, 0x1d, 0xf9, 0xf3, 0x1c, 0x8d, 0x87, 0x8b, 0x11, 0x55, 0xf4, 0x67, 0x1d, 0x51, 0xa1, 0x85, 0xc2, 0x0, 0x90, 0x86, 0x67, 0xf4, 0x49, 0x58, 0x70, 0x68, 0xa1, 0xa4, 0x74, 0x79, 0x70, 0x65, 0xa3, 0x70, 0x61, 0x79} + require.EqualValues(t, expectedBytes, txBytes) + expectedTxid := "HXNXFLSFZQXGOEN33IIQU4OAVZTGXIYHE4HPWFG3RDKKA4OM64JQ" + require.Equal(t, expectedTxid, txid) + + // decode and verify + var stx types.SignedTxn + err = msgpack.Decode(txBytes, &stx) + require.NoError(t, err) + + require.Equal(t, multisigAddr, stx.AuthAddr) + require.Equal(t, tx, stx.Txn) + + bytesToSign := rawTransactionBytesToSign(stx.Txn) + verified := VerifyMultisig(multisigAddr, bytesToSign, stx.Msig) + require.False(t, verified) // not enough signatures +} + func TestAppendMultisigTransaction(t *testing.T) { uniSigTxBytes := []byte{130, 164, 109, 115, 105, 103, 131, 166, 115, 117, 98, 115, 105, 103, 147, 130, 162, 112, 107, 196, 32, 27, 126, 192, 176, 75, 234, 97, 183, 150, 144, 151, 230, 203, 244, 7, 225, 8, 167, 5, 53, 29, 11, 201, 138, 190, 177, 34, 9, 168, 171, 129, 120, 161, 115, 196, 64, 118, 246, 119, 203, 209, 172, 34, 112, 79, 186, 215, 112, 41, 206, 201, 203, 230, 167, 215, 112, 156, 141, 37, 117, 149, 203, 209, 1, 132, 10, 96, 236, 87, 193, 248, 19, 228, 31, 230, 43, 94, 17, 231, 187, 158, 96, 148, 216, 202, 128, 206, 243, 48, 88, 234, 68, 38, 5, 169, 86, 146, 111, 121, 0, 129, 162, 112, 107, 196, 32, 9, 99, 50, 9, 83, 115, 137, 240, 117, 103, 17, 119, 57, 145, 199, 208, 62, 27, 115, 200, 196, 245, 43, 246, 175, 240, 26, 162, 92, 249, 194, 113, 129, 162, 112, 107, 196, 32, 231, 240, 248, 77, 6, 129, 29, 249, 243, 28, 141, 135, 139, 17, 85, 244, 103, 29, 81, 161, 133, 194, 0, 144, 134, 103, 244, 73, 88, 112, 104, 161, 163, 116, 104, 114, 2, 161, 118, 1, 163, 116, 120, 110, 137, 163, 97, 109, 116, 205, 19, 136, 163, 102, 101, 101, 206, 0, 3, 79, 168, 162, 102, 118, 206, 0, 14, 214, 220, 163, 103, 101, 110, 173, 116, 101, 115, 116, 110, 101, 116, 45, 118, 51, 49, 46, 48, 162, 108, 118, 206, 0, 14, 218, 196, 164, 110, 111, 116, 101, 196, 8, 180, 81, 121, 57, 252, 250, 210, 113, 163, 114, 99, 118, 196, 32, 27, 126, 192, 176, 75, 234, 97, 183, 150, 144, 151, 230, 203, 244, 7, 225, 8, 167, 5, 53, 29, 11, 201, 138, 190, 177, 34, 9, 168, 171, 129, 120, 163, 115, 110, 100, 196, 32, 141, 146, 180, 137, 144, 1, 115, 160, 77, 250, 67, 89, 163, 102, 106, 106, 252, 234, 44, 66, 160, 93, 217, 193, 247, 62, 235, 165, 71, 128, 55, 233, 164, 116, 121, 112, 101, 163, 112, 97, 121} ma, _, sk2, _ := makeTestMultisigAccount(t) @@ -95,6 +141,8 @@ func TestAppendMultisigTransaction(t *testing.T) { fromAddr, err := ma.Address() require.NoError(t, err) + require.Equal(t, types.Address{}, stx.AuthAddr) + verified := VerifyMultisig(fromAddr, bytesToSign, stx.Msig) require.True(t, verified) @@ -106,6 +154,38 @@ func TestAppendMultisigTransaction(t *testing.T) { require.False(t, verified) } +func TestAppendMultisigTransactionWithAuthAddr(t *testing.T) { + uniSigTxBytes := []byte{0x83, 0xa4, 0x6d, 0x73, 0x69, 0x67, 0x83, 0xa6, 0x73, 0x75, 0x62, 0x73, 0x69, 0x67, 0x93, 0x82, 0xa2, 0x70, 0x6b, 0xc4, 0x20, 0x1b, 0x7e, 0xc0, 0xb0, 0x4b, 0xea, 0x61, 0xb7, 0x96, 0x90, 0x97, 0xe6, 0xcb, 0xf4, 0x7, 0xe1, 0x8, 0xa7, 0x5, 0x35, 0x1d, 0xb, 0xc9, 0x8a, 0xbe, 0xb1, 0x22, 0x9, 0xa8, 0xab, 0x81, 0x78, 0xa1, 0x73, 0xc4, 0x40, 0x3e, 0xd3, 0xf2, 0x25, 0x45, 0x61, 0x38, 0x4a, 0x72, 0xca, 0x59, 0x2b, 0xd7, 0xca, 0xfa, 0x7, 0xe, 0x1, 0xb1, 0xce, 0x3c, 0x2e, 0xd8, 0x8c, 0x70, 0x80, 0xa7, 0x9d, 0xe2, 0x88, 0xf7, 0xa2, 0xa3, 0xc9, 0xee, 0x88, 0x0, 0xb, 0x6c, 0x2c, 0x6a, 0x7, 0xca, 0x53, 0x22, 0xcb, 0xc, 0x78, 0x82, 0xcd, 0x42, 0xc9, 0x76, 0x6a, 0x72, 0xed, 0x3f, 0x7c, 0x8a, 0x47, 0x47, 0xaf, 0xb7, 0x0, 0x81, 0xa2, 0x70, 0x6b, 0xc4, 0x20, 0x9, 0x63, 0x32, 0x9, 0x53, 0x73, 0x89, 0xf0, 0x75, 0x67, 0x11, 0x77, 0x39, 0x91, 0xc7, 0xd0, 0x3e, 0x1b, 0x73, 0xc8, 0xc4, 0xf5, 0x2b, 0xf6, 0xaf, 0xf0, 0x1a, 0xa2, 0x5c, 0xf9, 0xc2, 0x71, 0x81, 0xa2, 0x70, 0x6b, 0xc4, 0x20, 0xe7, 0xf0, 0xf8, 0x4d, 0x6, 0x81, 0x1d, 0xf9, 0xf3, 0x1c, 0x8d, 0x87, 0x8b, 0x11, 0x55, 0xf4, 0x67, 0x1d, 0x51, 0xa1, 0x85, 0xc2, 0x0, 0x90, 0x86, 0x67, 0xf4, 0x49, 0x58, 0x70, 0x68, 0xa1, 0xa3, 0x74, 0x68, 0x72, 0x2, 0xa1, 0x76, 0x1, 0xa4, 0x73, 0x67, 0x6e, 0x72, 0xc4, 0x20, 0x8d, 0x92, 0xb4, 0x89, 0x90, 0x1, 0x73, 0xa0, 0x4d, 0xfa, 0x43, 0x59, 0xa3, 0x66, 0x6a, 0x6a, 0xfc, 0xea, 0x2c, 0x42, 0xa0, 0x5d, 0xd9, 0xc1, 0xf7, 0x3e, 0xeb, 0xa5, 0x47, 0x80, 0x37, 0xe9, 0xa3, 0x74, 0x78, 0x6e, 0x89, 0xa3, 0x61, 0x6d, 0x74, 0xcd, 0x13, 0x88, 0xa3, 0x66, 0x65, 0x65, 0xce, 0x0, 0x3, 0x4f, 0xa8, 0xa2, 0x66, 0x76, 0xce, 0x0, 0xe, 0xd6, 0xdc, 0xa3, 0x67, 0x65, 0x6e, 0xad, 0x74, 0x65, 0x73, 0x74, 0x6e, 0x65, 0x74, 0x2d, 0x76, 0x33, 0x31, 0x2e, 0x30, 0xa2, 0x6c, 0x76, 0xce, 0x0, 0xe, 0xda, 0xc4, 0xa4, 0x6e, 0x6f, 0x74, 0x65, 0xc4, 0x8, 0xb4, 0x51, 0x79, 0x39, 0xfc, 0xfa, 0xd2, 0x71, 0xa3, 0x72, 0x63, 0x76, 0xc4, 0x20, 0x1b, 0x7e, 0xc0, 0xb0, 0x4b, 0xea, 0x61, 0xb7, 0x96, 0x90, 0x97, 0xe6, 0xcb, 0xf4, 0x7, 0xe1, 0x8, 0xa7, 0x5, 0x35, 0x1d, 0xb, 0xc9, 0x8a, 0xbe, 0xb1, 0x22, 0x9, 0xa8, 0xab, 0x81, 0x78, 0xa3, 0x73, 0x6e, 0x64, 0xc4, 0x20, 0xe7, 0xf0, 0xf8, 0x4d, 0x6, 0x81, 0x1d, 0xf9, 0xf3, 0x1c, 0x8d, 0x87, 0x8b, 0x11, 0x55, 0xf4, 0x67, 0x1d, 0x51, 0xa1, 0x85, 0xc2, 0x0, 0x90, 0x86, 0x67, 0xf4, 0x49, 0x58, 0x70, 0x68, 0xa1, 0xa4, 0x74, 0x79, 0x70, 0x65, 0xa3, 0x70, 0x61, 0x79} + ma, _, sk2, _ := makeTestMultisigAccount(t) + txid, txBytes, err := AppendMultisigTransaction(sk2, ma, uniSigTxBytes) + require.NoError(t, err) + expectedBytes := []byte{0x83, 0xa4, 0x6d, 0x73, 0x69, 0x67, 0x83, 0xa6, 0x73, 0x75, 0x62, 0x73, 0x69, 0x67, 0x93, 0x82, 0xa2, 0x70, 0x6b, 0xc4, 0x20, 0x1b, 0x7e, 0xc0, 0xb0, 0x4b, 0xea, 0x61, 0xb7, 0x96, 0x90, 0x97, 0xe6, 0xcb, 0xf4, 0x7, 0xe1, 0x8, 0xa7, 0x5, 0x35, 0x1d, 0xb, 0xc9, 0x8a, 0xbe, 0xb1, 0x22, 0x9, 0xa8, 0xab, 0x81, 0x78, 0xa1, 0x73, 0xc4, 0x40, 0x3e, 0xd3, 0xf2, 0x25, 0x45, 0x61, 0x38, 0x4a, 0x72, 0xca, 0x59, 0x2b, 0xd7, 0xca, 0xfa, 0x7, 0xe, 0x1, 0xb1, 0xce, 0x3c, 0x2e, 0xd8, 0x8c, 0x70, 0x80, 0xa7, 0x9d, 0xe2, 0x88, 0xf7, 0xa2, 0xa3, 0xc9, 0xee, 0x88, 0x0, 0xb, 0x6c, 0x2c, 0x6a, 0x7, 0xca, 0x53, 0x22, 0xcb, 0xc, 0x78, 0x82, 0xcd, 0x42, 0xc9, 0x76, 0x6a, 0x72, 0xed, 0x3f, 0x7c, 0x8a, 0x47, 0x47, 0xaf, 0xb7, 0x0, 0x82, 0xa2, 0x70, 0x6b, 0xc4, 0x20, 0x9, 0x63, 0x32, 0x9, 0x53, 0x73, 0x89, 0xf0, 0x75, 0x67, 0x11, 0x77, 0x39, 0x91, 0xc7, 0xd0, 0x3e, 0x1b, 0x73, 0xc8, 0xc4, 0xf5, 0x2b, 0xf6, 0xaf, 0xf0, 0x1a, 0xa2, 0x5c, 0xf9, 0xc2, 0x71, 0xa1, 0x73, 0xc4, 0x40, 0x9c, 0x87, 0x6e, 0x6b, 0x4d, 0xbe, 0x87, 0xba, 0xa2, 0x4e, 0x40, 0x73, 0x73, 0x87, 0xe2, 0xe9, 0x8e, 0x5b, 0x2e, 0x5d, 0x24, 0x93, 0x3c, 0x59, 0x72, 0x31, 0xe0, 0x2f, 0x23, 0x4, 0x6a, 0xa1, 0xd, 0x16, 0x7e, 0x84, 0x32, 0x16, 0x14, 0xe, 0xb2, 0x6f, 0x4, 0x5b, 0xba, 0x9f, 0x39, 0x54, 0x1c, 0x46, 0x58, 0xcc, 0xc4, 0x7e, 0x51, 0x2e, 0xd, 0xee, 0x93, 0xf6, 0xec, 0x66, 0xfa, 0xf, 0x81, 0xa2, 0x70, 0x6b, 0xc4, 0x20, 0xe7, 0xf0, 0xf8, 0x4d, 0x6, 0x81, 0x1d, 0xf9, 0xf3, 0x1c, 0x8d, 0x87, 0x8b, 0x11, 0x55, 0xf4, 0x67, 0x1d, 0x51, 0xa1, 0x85, 0xc2, 0x0, 0x90, 0x86, 0x67, 0xf4, 0x49, 0x58, 0x70, 0x68, 0xa1, 0xa3, 0x74, 0x68, 0x72, 0x2, 0xa1, 0x76, 0x1, 0xa4, 0x73, 0x67, 0x6e, 0x72, 0xc4, 0x20, 0x8d, 0x92, 0xb4, 0x89, 0x90, 0x1, 0x73, 0xa0, 0x4d, 0xfa, 0x43, 0x59, 0xa3, 0x66, 0x6a, 0x6a, 0xfc, 0xea, 0x2c, 0x42, 0xa0, 0x5d, 0xd9, 0xc1, 0xf7, 0x3e, 0xeb, 0xa5, 0x47, 0x80, 0x37, 0xe9, 0xa3, 0x74, 0x78, 0x6e, 0x89, 0xa3, 0x61, 0x6d, 0x74, 0xcd, 0x13, 0x88, 0xa3, 0x66, 0x65, 0x65, 0xce, 0x0, 0x3, 0x4f, 0xa8, 0xa2, 0x66, 0x76, 0xce, 0x0, 0xe, 0xd6, 0xdc, 0xa3, 0x67, 0x65, 0x6e, 0xad, 0x74, 0x65, 0x73, 0x74, 0x6e, 0x65, 0x74, 0x2d, 0x76, 0x33, 0x31, 0x2e, 0x30, 0xa2, 0x6c, 0x76, 0xce, 0x0, 0xe, 0xda, 0xc4, 0xa4, 0x6e, 0x6f, 0x74, 0x65, 0xc4, 0x8, 0xb4, 0x51, 0x79, 0x39, 0xfc, 0xfa, 0xd2, 0x71, 0xa3, 0x72, 0x63, 0x76, 0xc4, 0x20, 0x1b, 0x7e, 0xc0, 0xb0, 0x4b, 0xea, 0x61, 0xb7, 0x96, 0x90, 0x97, 0xe6, 0xcb, 0xf4, 0x7, 0xe1, 0x8, 0xa7, 0x5, 0x35, 0x1d, 0xb, 0xc9, 0x8a, 0xbe, 0xb1, 0x22, 0x9, 0xa8, 0xab, 0x81, 0x78, 0xa3, 0x73, 0x6e, 0x64, 0xc4, 0x20, 0xe7, 0xf0, 0xf8, 0x4d, 0x6, 0x81, 0x1d, 0xf9, 0xf3, 0x1c, 0x8d, 0x87, 0x8b, 0x11, 0x55, 0xf4, 0x67, 0x1d, 0x51, 0xa1, 0x85, 0xc2, 0x0, 0x90, 0x86, 0x67, 0xf4, 0x49, 0x58, 0x70, 0x68, 0xa1, 0xa4, 0x74, 0x79, 0x70, 0x65, 0xa3, 0x70, 0x61, 0x79} + require.EqualValues(t, expectedBytes, txBytes) + expectedTxid := "HXNXFLSFZQXGOEN33IIQU4OAVZTGXIYHE4HPWFG3RDKKA4OM64JQ" + require.Equal(t, expectedTxid, txid) + + // decode and verify + var stx types.SignedTxn + err = msgpack.Decode(txBytes, &stx) + require.NoError(t, err) + bytesToSign := rawTransactionBytesToSign(stx.Txn) + + multisigAddr, err := ma.Address() + require.NoError(t, err) + + require.Equal(t, multisigAddr, stx.AuthAddr) + + verified := VerifyMultisig(multisigAddr, bytesToSign, stx.Msig) + require.True(t, verified) + + // now change fee and ensure signature verification fails + idx := bytes.IndexAny(bytesToSign, "fee") + require.NotEqual(t, -1, idx) + bytesToSign[idx+4] = 1 + verified = VerifyMultisig(multisigAddr, bytesToSign, stx.Msig) + require.False(t, verified) +} + func TestSignMultisigTransactionKeyReg(t *testing.T) { rawKeyRegTx := []byte{129, 163, 116, 120, 110, 137, 163, 102, 101, 101, 206, 0, 3, 200, 192, 162, 102, 118, 206, 0, 14, 249, 218, 162, 108, 118, 206, 0, 14, 253, 194, 166, 115, 101, 108, 107, 101, 121, 196, 32, 50, 18, 43, 43, 214, 61, 220, 83, 49, 150, 23, 165, 170, 83, 196, 177, 194, 111, 227, 220, 202, 242, 141, 54, 34, 181, 105, 119, 161, 64, 92, 134, 163, 115, 110, 100, 196, 32, 141, 146, 180, 137, 144, 1, 115, 160, 77, 250, 67, 89, 163, 102, 106, 106, 252, 234, 44, 66, 160, 93, 217, 193, 247, 62, 235, 165, 71, 128, 55, 233, 164, 116, 121, 112, 101, 166, 107, 101, 121, 114, 101, 103, 166, 118, 111, 116, 101, 107, 100, 205, 39, 16, 167, 118, 111, 116, 101, 107, 101, 121, 196, 32, 112, 27, 215, 251, 145, 43, 7, 179, 8, 17, 255, 40, 29, 159, 238, 149, 99, 229, 128, 46, 32, 38, 137, 35, 25, 37, 143, 119, 250, 147, 30, 136, 167, 118, 111, 116, 101, 108, 115, 116, 206, 0, 15, 66, 64} var tx types.SignedTxn @@ -140,12 +220,49 @@ func TestMergeMultisigTransactions(t *testing.T) { require.NoError(t, err) txid, txBytesSym2, err := MergeMultisigTransactions(oneThreeSigTxBytes, twoThreeSigTxBytes) require.NoError(t, err) + // make sure merging is symmetric require.EqualValues(t, txBytesSym2, txBytesSym) + // make sure they match expected output require.EqualValues(t, expectedBytes, txBytesSym) expectedTxid := "2U2QKCYSYA3DUJNVP5T2KWBLWVUMX4PPIGN43IPPVGVZKTNFKJFQ" require.Equal(t, expectedTxid, txid) + + var stx types.SignedTxn + err = msgpack.Decode(txBytesSym, &stx) + require.NoError(t, err) + + expectedAuthAddr := types.Address{} + require.Equal(t, expectedAuthAddr, stx.AuthAddr) +} + +func TestMergeMultisigTransactionsWithAuthAddr(t *testing.T) { + oneThreeSigTxBytes := []byte{0x83, 0xa4, 0x6d, 0x73, 0x69, 0x67, 0x83, 0xa6, 0x73, 0x75, 0x62, 0x73, 0x69, 0x67, 0x93, 0x82, 0xa2, 0x70, 0x6b, 0xc4, 0x20, 0x1b, 0x7e, 0xc0, 0xb0, 0x4b, 0xea, 0x61, 0xb7, 0x96, 0x90, 0x97, 0xe6, 0xcb, 0xf4, 0x7, 0xe1, 0x8, 0xa7, 0x5, 0x35, 0x1d, 0xb, 0xc9, 0x8a, 0xbe, 0xb1, 0x22, 0x9, 0xa8, 0xab, 0x81, 0x78, 0xa1, 0x73, 0xc4, 0x40, 0x3e, 0xd3, 0xf2, 0x25, 0x45, 0x61, 0x38, 0x4a, 0x72, 0xca, 0x59, 0x2b, 0xd7, 0xca, 0xfa, 0x7, 0xe, 0x1, 0xb1, 0xce, 0x3c, 0x2e, 0xd8, 0x8c, 0x70, 0x80, 0xa7, 0x9d, 0xe2, 0x88, 0xf7, 0xa2, 0xa3, 0xc9, 0xee, 0x88, 0x0, 0xb, 0x6c, 0x2c, 0x6a, 0x7, 0xca, 0x53, 0x22, 0xcb, 0xc, 0x78, 0x82, 0xcd, 0x42, 0xc9, 0x76, 0x6a, 0x72, 0xed, 0x3f, 0x7c, 0x8a, 0x47, 0x47, 0xaf, 0xb7, 0x0, 0x81, 0xa2, 0x70, 0x6b, 0xc4, 0x20, 0x9, 0x63, 0x32, 0x9, 0x53, 0x73, 0x89, 0xf0, 0x75, 0x67, 0x11, 0x77, 0x39, 0x91, 0xc7, 0xd0, 0x3e, 0x1b, 0x73, 0xc8, 0xc4, 0xf5, 0x2b, 0xf6, 0xaf, 0xf0, 0x1a, 0xa2, 0x5c, 0xf9, 0xc2, 0x71, 0x82, 0xa2, 0x70, 0x6b, 0xc4, 0x20, 0xe7, 0xf0, 0xf8, 0x4d, 0x6, 0x81, 0x1d, 0xf9, 0xf3, 0x1c, 0x8d, 0x87, 0x8b, 0x11, 0x55, 0xf4, 0x67, 0x1d, 0x51, 0xa1, 0x85, 0xc2, 0x0, 0x90, 0x86, 0x67, 0xf4, 0x49, 0x58, 0x70, 0x68, 0xa1, 0xa1, 0x73, 0xc4, 0x40, 0xb6, 0xf, 0x58, 0x9c, 0xf4, 0xaf, 0xe2, 0xc2, 0xed, 0xf1, 0xba, 0xc5, 0xd6, 0x86, 0x71, 0x18, 0x22, 0xba, 0xf6, 0x6f, 0xbc, 0x84, 0x72, 0x84, 0xb8, 0xce, 0x5a, 0xd8, 0xb1, 0x2c, 0xba, 0x3f, 0xf0, 0x5, 0x20, 0x44, 0xf2, 0x82, 0xff, 0x37, 0x82, 0xfc, 0xb4, 0xf2, 0xd9, 0x53, 0x18, 0xd2, 0xec, 0x5a, 0x9e, 0x5d, 0x0, 0x28, 0x79, 0x41, 0x69, 0xcf, 0xc6, 0x7f, 0x73, 0x99, 0xa0, 0x4, 0xa3, 0x74, 0x68, 0x72, 0x2, 0xa1, 0x76, 0x1, 0xa4, 0x73, 0x67, 0x6e, 0x72, 0xc4, 0x20, 0x8d, 0x92, 0xb4, 0x89, 0x90, 0x1, 0x73, 0xa0, 0x4d, 0xfa, 0x43, 0x59, 0xa3, 0x66, 0x6a, 0x6a, 0xfc, 0xea, 0x2c, 0x42, 0xa0, 0x5d, 0xd9, 0xc1, 0xf7, 0x3e, 0xeb, 0xa5, 0x47, 0x80, 0x37, 0xe9, 0xa3, 0x74, 0x78, 0x6e, 0x89, 0xa3, 0x61, 0x6d, 0x74, 0xcd, 0x13, 0x88, 0xa3, 0x66, 0x65, 0x65, 0xce, 0x0, 0x3, 0x4f, 0xa8, 0xa2, 0x66, 0x76, 0xce, 0x0, 0xe, 0xd6, 0xdc, 0xa3, 0x67, 0x65, 0x6e, 0xad, 0x74, 0x65, 0x73, 0x74, 0x6e, 0x65, 0x74, 0x2d, 0x76, 0x33, 0x31, 0x2e, 0x30, 0xa2, 0x6c, 0x76, 0xce, 0x0, 0xe, 0xda, 0xc4, 0xa4, 0x6e, 0x6f, 0x74, 0x65, 0xc4, 0x8, 0xb4, 0x51, 0x79, 0x39, 0xfc, 0xfa, 0xd2, 0x71, 0xa3, 0x72, 0x63, 0x76, 0xc4, 0x20, 0x1b, 0x7e, 0xc0, 0xb0, 0x4b, 0xea, 0x61, 0xb7, 0x96, 0x90, 0x97, 0xe6, 0xcb, 0xf4, 0x7, 0xe1, 0x8, 0xa7, 0x5, 0x35, 0x1d, 0xb, 0xc9, 0x8a, 0xbe, 0xb1, 0x22, 0x9, 0xa8, 0xab, 0x81, 0x78, 0xa3, 0x73, 0x6e, 0x64, 0xc4, 0x20, 0xe7, 0xf0, 0xf8, 0x4d, 0x6, 0x81, 0x1d, 0xf9, 0xf3, 0x1c, 0x8d, 0x87, 0x8b, 0x11, 0x55, 0xf4, 0x67, 0x1d, 0x51, 0xa1, 0x85, 0xc2, 0x0, 0x90, 0x86, 0x67, 0xf4, 0x49, 0x58, 0x70, 0x68, 0xa1, 0xa4, 0x74, 0x79, 0x70, 0x65, 0xa3, 0x70, 0x61, 0x79} + twoThreeSigTxBytes := []byte{0x83, 0xa4, 0x6d, 0x73, 0x69, 0x67, 0x83, 0xa6, 0x73, 0x75, 0x62, 0x73, 0x69, 0x67, 0x93, 0x81, 0xa2, 0x70, 0x6b, 0xc4, 0x20, 0x1b, 0x7e, 0xc0, 0xb0, 0x4b, 0xea, 0x61, 0xb7, 0x96, 0x90, 0x97, 0xe6, 0xcb, 0xf4, 0x7, 0xe1, 0x8, 0xa7, 0x5, 0x35, 0x1d, 0xb, 0xc9, 0x8a, 0xbe, 0xb1, 0x22, 0x9, 0xa8, 0xab, 0x81, 0x78, 0x82, 0xa2, 0x70, 0x6b, 0xc4, 0x20, 0x9, 0x63, 0x32, 0x9, 0x53, 0x73, 0x89, 0xf0, 0x75, 0x67, 0x11, 0x77, 0x39, 0x91, 0xc7, 0xd0, 0x3e, 0x1b, 0x73, 0xc8, 0xc4, 0xf5, 0x2b, 0xf6, 0xaf, 0xf0, 0x1a, 0xa2, 0x5c, 0xf9, 0xc2, 0x71, 0xa1, 0x73, 0xc4, 0x40, 0x9c, 0x87, 0x6e, 0x6b, 0x4d, 0xbe, 0x87, 0xba, 0xa2, 0x4e, 0x40, 0x73, 0x73, 0x87, 0xe2, 0xe9, 0x8e, 0x5b, 0x2e, 0x5d, 0x24, 0x93, 0x3c, 0x59, 0x72, 0x31, 0xe0, 0x2f, 0x23, 0x4, 0x6a, 0xa1, 0xd, 0x16, 0x7e, 0x84, 0x32, 0x16, 0x14, 0xe, 0xb2, 0x6f, 0x4, 0x5b, 0xba, 0x9f, 0x39, 0x54, 0x1c, 0x46, 0x58, 0xcc, 0xc4, 0x7e, 0x51, 0x2e, 0xd, 0xee, 0x93, 0xf6, 0xec, 0x66, 0xfa, 0xf, 0x82, 0xa2, 0x70, 0x6b, 0xc4, 0x20, 0xe7, 0xf0, 0xf8, 0x4d, 0x6, 0x81, 0x1d, 0xf9, 0xf3, 0x1c, 0x8d, 0x87, 0x8b, 0x11, 0x55, 0xf4, 0x67, 0x1d, 0x51, 0xa1, 0x85, 0xc2, 0x0, 0x90, 0x86, 0x67, 0xf4, 0x49, 0x58, 0x70, 0x68, 0xa1, 0xa1, 0x73, 0xc4, 0x40, 0xb6, 0xf, 0x58, 0x9c, 0xf4, 0xaf, 0xe2, 0xc2, 0xed, 0xf1, 0xba, 0xc5, 0xd6, 0x86, 0x71, 0x18, 0x22, 0xba, 0xf6, 0x6f, 0xbc, 0x84, 0x72, 0x84, 0xb8, 0xce, 0x5a, 0xd8, 0xb1, 0x2c, 0xba, 0x3f, 0xf0, 0x5, 0x20, 0x44, 0xf2, 0x82, 0xff, 0x37, 0x82, 0xfc, 0xb4, 0xf2, 0xd9, 0x53, 0x18, 0xd2, 0xec, 0x5a, 0x9e, 0x5d, 0x0, 0x28, 0x79, 0x41, 0x69, 0xcf, 0xc6, 0x7f, 0x73, 0x99, 0xa0, 0x4, 0xa3, 0x74, 0x68, 0x72, 0x2, 0xa1, 0x76, 0x1, 0xa4, 0x73, 0x67, 0x6e, 0x72, 0xc4, 0x20, 0x8d, 0x92, 0xb4, 0x89, 0x90, 0x1, 0x73, 0xa0, 0x4d, 0xfa, 0x43, 0x59, 0xa3, 0x66, 0x6a, 0x6a, 0xfc, 0xea, 0x2c, 0x42, 0xa0, 0x5d, 0xd9, 0xc1, 0xf7, 0x3e, 0xeb, 0xa5, 0x47, 0x80, 0x37, 0xe9, 0xa3, 0x74, 0x78, 0x6e, 0x89, 0xa3, 0x61, 0x6d, 0x74, 0xcd, 0x13, 0x88, 0xa3, 0x66, 0x65, 0x65, 0xce, 0x0, 0x3, 0x4f, 0xa8, 0xa2, 0x66, 0x76, 0xce, 0x0, 0xe, 0xd6, 0xdc, 0xa3, 0x67, 0x65, 0x6e, 0xad, 0x74, 0x65, 0x73, 0x74, 0x6e, 0x65, 0x74, 0x2d, 0x76, 0x33, 0x31, 0x2e, 0x30, 0xa2, 0x6c, 0x76, 0xce, 0x0, 0xe, 0xda, 0xc4, 0xa4, 0x6e, 0x6f, 0x74, 0x65, 0xc4, 0x8, 0xb4, 0x51, 0x79, 0x39, 0xfc, 0xfa, 0xd2, 0x71, 0xa3, 0x72, 0x63, 0x76, 0xc4, 0x20, 0x1b, 0x7e, 0xc0, 0xb0, 0x4b, 0xea, 0x61, 0xb7, 0x96, 0x90, 0x97, 0xe6, 0xcb, 0xf4, 0x7, 0xe1, 0x8, 0xa7, 0x5, 0x35, 0x1d, 0xb, 0xc9, 0x8a, 0xbe, 0xb1, 0x22, 0x9, 0xa8, 0xab, 0x81, 0x78, 0xa3, 0x73, 0x6e, 0x64, 0xc4, 0x20, 0xe7, 0xf0, 0xf8, 0x4d, 0x6, 0x81, 0x1d, 0xf9, 0xf3, 0x1c, 0x8d, 0x87, 0x8b, 0x11, 0x55, 0xf4, 0x67, 0x1d, 0x51, 0xa1, 0x85, 0xc2, 0x0, 0x90, 0x86, 0x67, 0xf4, 0x49, 0x58, 0x70, 0x68, 0xa1, 0xa4, 0x74, 0x79, 0x70, 0x65, 0xa3, 0x70, 0x61, 0x79} + expectedBytes := []byte{0x83, 0xa4, 0x6d, 0x73, 0x69, 0x67, 0x83, 0xa6, 0x73, 0x75, 0x62, 0x73, 0x69, 0x67, 0x93, 0x82, 0xa2, 0x70, 0x6b, 0xc4, 0x20, 0x1b, 0x7e, 0xc0, 0xb0, 0x4b, 0xea, 0x61, 0xb7, 0x96, 0x90, 0x97, 0xe6, 0xcb, 0xf4, 0x7, 0xe1, 0x8, 0xa7, 0x5, 0x35, 0x1d, 0xb, 0xc9, 0x8a, 0xbe, 0xb1, 0x22, 0x9, 0xa8, 0xab, 0x81, 0x78, 0xa1, 0x73, 0xc4, 0x40, 0x3e, 0xd3, 0xf2, 0x25, 0x45, 0x61, 0x38, 0x4a, 0x72, 0xca, 0x59, 0x2b, 0xd7, 0xca, 0xfa, 0x7, 0xe, 0x1, 0xb1, 0xce, 0x3c, 0x2e, 0xd8, 0x8c, 0x70, 0x80, 0xa7, 0x9d, 0xe2, 0x88, 0xf7, 0xa2, 0xa3, 0xc9, 0xee, 0x88, 0x0, 0xb, 0x6c, 0x2c, 0x6a, 0x7, 0xca, 0x53, 0x22, 0xcb, 0xc, 0x78, 0x82, 0xcd, 0x42, 0xc9, 0x76, 0x6a, 0x72, 0xed, 0x3f, 0x7c, 0x8a, 0x47, 0x47, 0xaf, 0xb7, 0x0, 0x82, 0xa2, 0x70, 0x6b, 0xc4, 0x20, 0x9, 0x63, 0x32, 0x9, 0x53, 0x73, 0x89, 0xf0, 0x75, 0x67, 0x11, 0x77, 0x39, 0x91, 0xc7, 0xd0, 0x3e, 0x1b, 0x73, 0xc8, 0xc4, 0xf5, 0x2b, 0xf6, 0xaf, 0xf0, 0x1a, 0xa2, 0x5c, 0xf9, 0xc2, 0x71, 0xa1, 0x73, 0xc4, 0x40, 0x9c, 0x87, 0x6e, 0x6b, 0x4d, 0xbe, 0x87, 0xba, 0xa2, 0x4e, 0x40, 0x73, 0x73, 0x87, 0xe2, 0xe9, 0x8e, 0x5b, 0x2e, 0x5d, 0x24, 0x93, 0x3c, 0x59, 0x72, 0x31, 0xe0, 0x2f, 0x23, 0x4, 0x6a, 0xa1, 0xd, 0x16, 0x7e, 0x84, 0x32, 0x16, 0x14, 0xe, 0xb2, 0x6f, 0x4, 0x5b, 0xba, 0x9f, 0x39, 0x54, 0x1c, 0x46, 0x58, 0xcc, 0xc4, 0x7e, 0x51, 0x2e, 0xd, 0xee, 0x93, 0xf6, 0xec, 0x66, 0xfa, 0xf, 0x82, 0xa2, 0x70, 0x6b, 0xc4, 0x20, 0xe7, 0xf0, 0xf8, 0x4d, 0x6, 0x81, 0x1d, 0xf9, 0xf3, 0x1c, 0x8d, 0x87, 0x8b, 0x11, 0x55, 0xf4, 0x67, 0x1d, 0x51, 0xa1, 0x85, 0xc2, 0x0, 0x90, 0x86, 0x67, 0xf4, 0x49, 0x58, 0x70, 0x68, 0xa1, 0xa1, 0x73, 0xc4, 0x40, 0xb6, 0xf, 0x58, 0x9c, 0xf4, 0xaf, 0xe2, 0xc2, 0xed, 0xf1, 0xba, 0xc5, 0xd6, 0x86, 0x71, 0x18, 0x22, 0xba, 0xf6, 0x6f, 0xbc, 0x84, 0x72, 0x84, 0xb8, 0xce, 0x5a, 0xd8, 0xb1, 0x2c, 0xba, 0x3f, 0xf0, 0x5, 0x20, 0x44, 0xf2, 0x82, 0xff, 0x37, 0x82, 0xfc, 0xb4, 0xf2, 0xd9, 0x53, 0x18, 0xd2, 0xec, 0x5a, 0x9e, 0x5d, 0x0, 0x28, 0x79, 0x41, 0x69, 0xcf, 0xc6, 0x7f, 0x73, 0x99, 0xa0, 0x4, 0xa3, 0x74, 0x68, 0x72, 0x2, 0xa1, 0x76, 0x1, 0xa4, 0x73, 0x67, 0x6e, 0x72, 0xc4, 0x20, 0x8d, 0x92, 0xb4, 0x89, 0x90, 0x1, 0x73, 0xa0, 0x4d, 0xfa, 0x43, 0x59, 0xa3, 0x66, 0x6a, 0x6a, 0xfc, 0xea, 0x2c, 0x42, 0xa0, 0x5d, 0xd9, 0xc1, 0xf7, 0x3e, 0xeb, 0xa5, 0x47, 0x80, 0x37, 0xe9, 0xa3, 0x74, 0x78, 0x6e, 0x89, 0xa3, 0x61, 0x6d, 0x74, 0xcd, 0x13, 0x88, 0xa3, 0x66, 0x65, 0x65, 0xce, 0x0, 0x3, 0x4f, 0xa8, 0xa2, 0x66, 0x76, 0xce, 0x0, 0xe, 0xd6, 0xdc, 0xa3, 0x67, 0x65, 0x6e, 0xad, 0x74, 0x65, 0x73, 0x74, 0x6e, 0x65, 0x74, 0x2d, 0x76, 0x33, 0x31, 0x2e, 0x30, 0xa2, 0x6c, 0x76, 0xce, 0x0, 0xe, 0xda, 0xc4, 0xa4, 0x6e, 0x6f, 0x74, 0x65, 0xc4, 0x8, 0xb4, 0x51, 0x79, 0x39, 0xfc, 0xfa, 0xd2, 0x71, 0xa3, 0x72, 0x63, 0x76, 0xc4, 0x20, 0x1b, 0x7e, 0xc0, 0xb0, 0x4b, 0xea, 0x61, 0xb7, 0x96, 0x90, 0x97, 0xe6, 0xcb, 0xf4, 0x7, 0xe1, 0x8, 0xa7, 0x5, 0x35, 0x1d, 0xb, 0xc9, 0x8a, 0xbe, 0xb1, 0x22, 0x9, 0xa8, 0xab, 0x81, 0x78, 0xa3, 0x73, 0x6e, 0x64, 0xc4, 0x20, 0xe7, 0xf0, 0xf8, 0x4d, 0x6, 0x81, 0x1d, 0xf9, 0xf3, 0x1c, 0x8d, 0x87, 0x8b, 0x11, 0x55, 0xf4, 0x67, 0x1d, 0x51, 0xa1, 0x85, 0xc2, 0x0, 0x90, 0x86, 0x67, 0xf4, 0x49, 0x58, 0x70, 0x68, 0xa1, 0xa4, 0x74, 0x79, 0x70, 0x65, 0xa3, 0x70, 0x61, 0x79} + + _, txBytesSym, err := MergeMultisigTransactions(twoThreeSigTxBytes, oneThreeSigTxBytes) + require.NoError(t, err) + txid, txBytesSym2, err := MergeMultisigTransactions(oneThreeSigTxBytes, twoThreeSigTxBytes) + require.NoError(t, err) + + // make sure merging is symmetric + require.EqualValues(t, txBytesSym2, txBytesSym) + + // make sure they match expected output + require.EqualValues(t, expectedBytes, txBytesSym) + expectedTxid := "HXNXFLSFZQXGOEN33IIQU4OAVZTGXIYHE4HPWFG3RDKKA4OM64JQ" + require.Equal(t, expectedTxid, txid) + + var stx types.SignedTxn + err = msgpack.Decode(txBytesSym, &stx) + require.NoError(t, err) + + expectedAuthAddr, err := types.DecodeAddress("RWJLJCMQAFZ2ATP2INM2GZTKNL6OULCCUBO5TQPXH3V2KR4AG7U5UA5JNM") + require.NoError(t, err) + + require.Equal(t, expectedAuthAddr, stx.AuthAddr) } func TestSignBytes(t *testing.T) { @@ -174,6 +291,7 @@ func TestMakeLogicSigBasic(t *testing.T) { lsig, err := MakeLogicSig(program, args, sk, pk) require.Error(t, err) require.Equal(t, types.LogicSig{}, lsig) + require.True(t, lsig.Blank()) program = []byte{1, 32, 1, 1, 34} programHash := "6Z3C3LDVWGMX23BMSYMANACQOSINPFIRF77H7N3AWJZYV6OH6GWTJKVMXY" @@ -210,16 +328,8 @@ func TestMakeLogicSigBasic(t *testing.T) { require.NoError(t, err) require.Equal(t, lsig, lsig1) - // check verification of modified program fails - programMod := make([]byte, len(program)) - copy(programMod[:], program) - programMod[3] = 2 - lsig, err = MakeLogicSig(programMod, args, sk, pk) - require.NoError(t, err) - verified = VerifyLogicSig(lsig, contractSender) - require.False(t, verified) - // check invalid program fails + programMod := make([]byte, len(program)) copy(programMod[:], program) programMod[0] = 128 lsig, err = MakeLogicSig(programMod, args, sk, pk) @@ -232,20 +342,28 @@ func TestMakeLogicSigSingle(t *testing.T) { var sk ed25519.PrivateKey var pk MultisigAccount - acc := GenerateAccount() + acc, err := AccountFromPrivateKey(ed25519.PrivateKey{0xd2, 0xdc, 0x4c, 0xcc, 0xe9, 0x98, 0x62, 0xff, 0xcf, 0x8c, 0xeb, 0x93, 0x6, 0xc4, 0x8d, 0xa6, 0x80, 0x50, 0x82, 0xa, 0xbb, 0x29, 0x95, 0x7a, 0xac, 0x82, 0x68, 0x9a, 0x8c, 0x49, 0x5a, 0x38, 0x5e, 0x67, 0x4f, 0x1c, 0xa, 0xee, 0xec, 0x37, 0x71, 0x89, 0x8f, 0x61, 0xc7, 0x6f, 0xf5, 0xd2, 0x4a, 0x19, 0x79, 0x3e, 0x2c, 0x91, 0xfa, 0x8, 0x51, 0x62, 0x63, 0xe3, 0x85, 0x73, 0xea, 0x42}) + require.NoError(t, err) program = []byte{1, 32, 1, 1, 34} sk = acc.PrivateKey lsig, err := MakeLogicSig(program, args, sk, pk) require.NoError(t, err) - require.NotEqual(t, types.Signature{}, lsig.Sig) + expectedSig := types.Signature{0x3e, 0x5, 0x3d, 0x39, 0x4d, 0xfb, 0x12, 0xbc, 0x65, 0x79, 0x9f, 0xea, 0x31, 0x8a, 0x7b, 0x8e, 0xa2, 0x51, 0x8b, 0x55, 0x2c, 0x8a, 0xbe, 0x6c, 0xd7, 0xa7, 0x65, 0x2d, 0xd8, 0xb0, 0x18, 0x7e, 0x21, 0x5, 0x2d, 0xb9, 0x24, 0x62, 0x89, 0x16, 0xe5, 0x61, 0x74, 0xcd, 0xf, 0x19, 0xac, 0xb9, 0x6c, 0x45, 0xa4, 0x29, 0x91, 0x99, 0x11, 0x1d, 0xe4, 0x7c, 0xe4, 0xfc, 0x12, 0xec, 0xce, 0x2} + require.Equal(t, expectedSig, lsig.Sig) require.True(t, lsig.Msig.Blank()) - var sender types.Address - copy(sender[:], acc.PublicKey) - - verified := VerifyLogicSig(lsig, sender) + verified := VerifyLogicSig(lsig, acc.Address) require.True(t, verified) + // check that a modified program fails verification + modProgram := make([]byte, len(program)) + copy(modProgram, program) + lsigModified, err := MakeLogicSig(modProgram, args, sk, pk) + require.NoError(t, err) + modProgram[3] = 2 + verified = VerifyLogicSig(lsigModified, acc.Address) + require.False(t, verified) + // check serialization var lsig1 types.LogicSig encoded := msgpack.Encode(lsig) @@ -286,6 +404,15 @@ func TestMakeLogicSigMulti(t *testing.T) { verified = VerifyLogicSig(lsig, sender) require.True(t, verified) + // check that a modified program fails verification + modProgram := make([]byte, len(program)) + copy(modProgram, program) + lsigModified, err := MakeLogicSig(modProgram, args, sk1, ma) + require.NoError(t, err) + modProgram[3] = 2 + verified = VerifyLogicSig(lsigModified, sender) + require.False(t, verified) + // combine sig and multisig, ensure it fails lsigf, err := MakeLogicSig(program, args, sk, pk) require.NoError(t, err) @@ -307,6 +434,272 @@ func TestMakeLogicSigMulti(t *testing.T) { require.Equal(t, lsig, lsig1) } +func TestSignLogicsigTransaction(t *testing.T) { + program := []byte{1, 32, 1, 1, 34} + args := [][]byte{ + {0x01}, + {0x02, 0x03}, + } + + otherAddrStr := "WTDCE2FEYM2VB5MKNXKLRSRDTSPR2EFTIGVH4GRW4PHGD6747GFJTBGT2A" + otherAddr, err := types.DecodeAddress(otherAddrStr) + require.NoError(t, err) + + testSign := func(t *testing.T, lsig types.LogicSig, sender types.Address, expectedBytes []byte, expectedTxid string, expectedAuthAddr types.Address) { + txn := types.Transaction{ + Type: types.PaymentTx, + Header: types.Header{ + Sender: sender, + Fee: 217000, + FirstValid: 972508, + LastValid: 973508, + Note: []byte{180, 81, 121, 57, 252, 250, 210, 113}, + GenesisID: "testnet-v31.0", + }, + PaymentTxnFields: types.PaymentTxnFields{ + Receiver: otherAddr, + Amount: 5000, + }, + } + + txid, stxnBytes, err := SignLogicsigTransaction(lsig, txn) + require.NoError(t, err) + require.EqualValues(t, expectedBytes, stxnBytes) + require.Equal(t, expectedTxid, txid) + + // decode and verify + var stx types.SignedTxn + err = msgpack.Decode(stxnBytes, &stx) + require.NoError(t, err) + + require.Equal(t, expectedAuthAddr, stx.AuthAddr) + + require.Equal(t, lsig, stx.Lsig) + require.Equal(t, types.Signature{}, stx.Sig) + require.True(t, stx.Msig.Blank()) + + require.Equal(t, txn, stx.Txn) + } + + t.Run("no sig", func(t *testing.T) { + var sk ed25519.PrivateKey + var ma MultisigAccount + lsig, err := MakeLogicSig(program, args, sk, ma) + require.NoError(t, err) + + programHash := "6Z3C3LDVWGMX23BMSYMANACQOSINPFIRF77H7N3AWJZYV6OH6GWTJKVMXY" + programAddr, err := types.DecodeAddress(programHash) + require.NoError(t, err) + + t.Run("sender is contract addr", func(t *testing.T) { + expectedBytes := []byte{0x82, 0xa4, 0x6c, 0x73, 0x69, 0x67, 0x82, 0xa3, 0x61, 0x72, 0x67, 0x92, 0xc4, 0x1, 0x1, 0xc4, 0x2, 0x2, 0x3, 0xa1, 0x6c, 0xc4, 0x5, 0x1, 0x20, 0x1, 0x1, 0x22, 0xa3, 0x74, 0x78, 0x6e, 0x89, 0xa3, 0x61, 0x6d, 0x74, 0xcd, 0x13, 0x88, 0xa3, 0x66, 0x65, 0x65, 0xce, 0x0, 0x3, 0x4f, 0xa8, 0xa2, 0x66, 0x76, 0xce, 0x0, 0xe, 0xd6, 0xdc, 0xa3, 0x67, 0x65, 0x6e, 0xad, 0x74, 0x65, 0x73, 0x74, 0x6e, 0x65, 0x74, 0x2d, 0x76, 0x33, 0x31, 0x2e, 0x30, 0xa2, 0x6c, 0x76, 0xce, 0x0, 0xe, 0xda, 0xc4, 0xa4, 0x6e, 0x6f, 0x74, 0x65, 0xc4, 0x8, 0xb4, 0x51, 0x79, 0x39, 0xfc, 0xfa, 0xd2, 0x71, 0xa3, 0x72, 0x63, 0x76, 0xc4, 0x20, 0xb4, 0xc6, 0x22, 0x68, 0xa4, 0xc3, 0x35, 0x50, 0xf5, 0x8a, 0x6d, 0xd4, 0xb8, 0xca, 0x23, 0x9c, 0x9f, 0x1d, 0x10, 0xb3, 0x41, 0xaa, 0x7e, 0x1a, 0x36, 0xe3, 0xce, 0x61, 0xfb, 0xfc, 0xf9, 0x8a, 0xa3, 0x73, 0x6e, 0x64, 0xc4, 0x20, 0xf6, 0x76, 0x2d, 0xac, 0x75, 0xb1, 0x99, 0x7d, 0x6c, 0x2c, 0x96, 0x18, 0x6, 0x80, 0x50, 0x74, 0x90, 0xd7, 0x95, 0x11, 0x2f, 0xfe, 0x7f, 0xb7, 0x60, 0xb2, 0x73, 0x8a, 0xf9, 0xc7, 0xf1, 0xad, 0xa4, 0x74, 0x79, 0x70, 0x65, 0xa3, 0x70, 0x61, 0x79} + expectedTxid := "IL5UCKXGWBA2MQ4YYFQKYC3BFCWO2KHZSNZWVDIXOOZS3AWVIQDA" + expectedAuthAddr := types.Address{} + sender := programAddr + testSign(t, lsig, sender, expectedBytes, expectedTxid, expectedAuthAddr) + }) + + t.Run("sender is not contract addr", func(t *testing.T) { + expectedBytes := []byte{0x83, 0xa4, 0x6c, 0x73, 0x69, 0x67, 0x82, 0xa3, 0x61, 0x72, 0x67, 0x92, 0xc4, 0x1, 0x1, 0xc4, 0x2, 0x2, 0x3, 0xa1, 0x6c, 0xc4, 0x5, 0x1, 0x20, 0x1, 0x1, 0x22, 0xa4, 0x73, 0x67, 0x6e, 0x72, 0xc4, 0x20, 0xf6, 0x76, 0x2d, 0xac, 0x75, 0xb1, 0x99, 0x7d, 0x6c, 0x2c, 0x96, 0x18, 0x6, 0x80, 0x50, 0x74, 0x90, 0xd7, 0x95, 0x11, 0x2f, 0xfe, 0x7f, 0xb7, 0x60, 0xb2, 0x73, 0x8a, 0xf9, 0xc7, 0xf1, 0xad, 0xa3, 0x74, 0x78, 0x6e, 0x89, 0xa3, 0x61, 0x6d, 0x74, 0xcd, 0x13, 0x88, 0xa3, 0x66, 0x65, 0x65, 0xce, 0x0, 0x3, 0x4f, 0xa8, 0xa2, 0x66, 0x76, 0xce, 0x0, 0xe, 0xd6, 0xdc, 0xa3, 0x67, 0x65, 0x6e, 0xad, 0x74, 0x65, 0x73, 0x74, 0x6e, 0x65, 0x74, 0x2d, 0x76, 0x33, 0x31, 0x2e, 0x30, 0xa2, 0x6c, 0x76, 0xce, 0x0, 0xe, 0xda, 0xc4, 0xa4, 0x6e, 0x6f, 0x74, 0x65, 0xc4, 0x8, 0xb4, 0x51, 0x79, 0x39, 0xfc, 0xfa, 0xd2, 0x71, 0xa3, 0x72, 0x63, 0x76, 0xc4, 0x20, 0xb4, 0xc6, 0x22, 0x68, 0xa4, 0xc3, 0x35, 0x50, 0xf5, 0x8a, 0x6d, 0xd4, 0xb8, 0xca, 0x23, 0x9c, 0x9f, 0x1d, 0x10, 0xb3, 0x41, 0xaa, 0x7e, 0x1a, 0x36, 0xe3, 0xce, 0x61, 0xfb, 0xfc, 0xf9, 0x8a, 0xa3, 0x73, 0x6e, 0x64, 0xc4, 0x20, 0xb4, 0xc6, 0x22, 0x68, 0xa4, 0xc3, 0x35, 0x50, 0xf5, 0x8a, 0x6d, 0xd4, 0xb8, 0xca, 0x23, 0x9c, 0x9f, 0x1d, 0x10, 0xb3, 0x41, 0xaa, 0x7e, 0x1a, 0x36, 0xe3, 0xce, 0x61, 0xfb, 0xfc, 0xf9, 0x8a, 0xa4, 0x74, 0x79, 0x70, 0x65, 0xa3, 0x70, 0x61, 0x79} + expectedTxid := "U4X24Q45MCZ6JSL343QNR3RC6AJO2NUDQXFCTNONIW3SU2AYQJOA" + expectedAuthAddr := programAddr + sender := otherAddr + testSign(t, lsig, sender, expectedBytes, expectedTxid, expectedAuthAddr) + }) + }) + + t.Run("single sig", func(t *testing.T) { + var ma MultisigAccount + acc, err := AccountFromPrivateKey(ed25519.PrivateKey{0xd2, 0xdc, 0x4c, 0xcc, 0xe9, 0x98, 0x62, 0xff, 0xcf, 0x8c, 0xeb, 0x93, 0x6, 0xc4, 0x8d, 0xa6, 0x80, 0x50, 0x82, 0xa, 0xbb, 0x29, 0x95, 0x7a, 0xac, 0x82, 0x68, 0x9a, 0x8c, 0x49, 0x5a, 0x38, 0x5e, 0x67, 0x4f, 0x1c, 0xa, 0xee, 0xec, 0x37, 0x71, 0x89, 0x8f, 0x61, 0xc7, 0x6f, 0xf5, 0xd2, 0x4a, 0x19, 0x79, 0x3e, 0x2c, 0x91, 0xfa, 0x8, 0x51, 0x62, 0x63, 0xe3, 0x85, 0x73, 0xea, 0x42}) + require.NoError(t, err) + lsig, err := MakeLogicSig(program, args, acc.PrivateKey, ma) + require.NoError(t, err) + + t.Run("sender is contract addr", func(t *testing.T) { + expectedBytes := []byte{0x82, 0xa4, 0x6c, 0x73, 0x69, 0x67, 0x83, 0xa3, 0x61, 0x72, 0x67, 0x92, 0xc4, 0x1, 0x1, 0xc4, 0x2, 0x2, 0x3, 0xa1, 0x6c, 0xc4, 0x5, 0x1, 0x20, 0x1, 0x1, 0x22, 0xa3, 0x73, 0x69, 0x67, 0xc4, 0x40, 0x3e, 0x5, 0x3d, 0x39, 0x4d, 0xfb, 0x12, 0xbc, 0x65, 0x79, 0x9f, 0xea, 0x31, 0x8a, 0x7b, 0x8e, 0xa2, 0x51, 0x8b, 0x55, 0x2c, 0x8a, 0xbe, 0x6c, 0xd7, 0xa7, 0x65, 0x2d, 0xd8, 0xb0, 0x18, 0x7e, 0x21, 0x5, 0x2d, 0xb9, 0x24, 0x62, 0x89, 0x16, 0xe5, 0x61, 0x74, 0xcd, 0xf, 0x19, 0xac, 0xb9, 0x6c, 0x45, 0xa4, 0x29, 0x91, 0x99, 0x11, 0x1d, 0xe4, 0x7c, 0xe4, 0xfc, 0x12, 0xec, 0xce, 0x2, 0xa3, 0x74, 0x78, 0x6e, 0x89, 0xa3, 0x61, 0x6d, 0x74, 0xcd, 0x13, 0x88, 0xa3, 0x66, 0x65, 0x65, 0xce, 0x0, 0x3, 0x4f, 0xa8, 0xa2, 0x66, 0x76, 0xce, 0x0, 0xe, 0xd6, 0xdc, 0xa3, 0x67, 0x65, 0x6e, 0xad, 0x74, 0x65, 0x73, 0x74, 0x6e, 0x65, 0x74, 0x2d, 0x76, 0x33, 0x31, 0x2e, 0x30, 0xa2, 0x6c, 0x76, 0xce, 0x0, 0xe, 0xda, 0xc4, 0xa4, 0x6e, 0x6f, 0x74, 0x65, 0xc4, 0x8, 0xb4, 0x51, 0x79, 0x39, 0xfc, 0xfa, 0xd2, 0x71, 0xa3, 0x72, 0x63, 0x76, 0xc4, 0x20, 0xb4, 0xc6, 0x22, 0x68, 0xa4, 0xc3, 0x35, 0x50, 0xf5, 0x8a, 0x6d, 0xd4, 0xb8, 0xca, 0x23, 0x9c, 0x9f, 0x1d, 0x10, 0xb3, 0x41, 0xaa, 0x7e, 0x1a, 0x36, 0xe3, 0xce, 0x61, 0xfb, 0xfc, 0xf9, 0x8a, 0xa3, 0x73, 0x6e, 0x64, 0xc4, 0x20, 0x5e, 0x67, 0x4f, 0x1c, 0xa, 0xee, 0xec, 0x37, 0x71, 0x89, 0x8f, 0x61, 0xc7, 0x6f, 0xf5, 0xd2, 0x4a, 0x19, 0x79, 0x3e, 0x2c, 0x91, 0xfa, 0x8, 0x51, 0x62, 0x63, 0xe3, 0x85, 0x73, 0xea, 0x42, 0xa4, 0x74, 0x79, 0x70, 0x65, 0xa3, 0x70, 0x61, 0x79} + expectedTxid := "XPFTYDOV5RCL7K5EGTC32PSTRKFO3ITZIL64CRTYIJEPHXOTCCXA" + expectedAuthAddr := types.Address{} + sender := acc.Address + testSign(t, lsig, sender, expectedBytes, expectedTxid, expectedAuthAddr) + }) + + t.Run("sender is not contract addr", func(t *testing.T) { + txn := types.Transaction{ + Type: types.PaymentTx, + Header: types.Header{ + Sender: otherAddr, + Fee: 217000, + FirstValid: 972508, + LastValid: 973508, + Note: []byte{180, 81, 121, 57, 252, 250, 210, 113}, + GenesisID: "testnet-v31.0", + }, + PaymentTxnFields: types.PaymentTxnFields{ + Receiver: otherAddr, + Amount: 5000, + }, + } + + _, _, err := SignLogicsigTransaction(lsig, txn) + require.Error(t, err, errLsigInvalidSignature) + }) + }) + + t.Run("multi sig", func(t *testing.T) { + ma, sk1, sk2, _ := makeTestMultisigAccount(t) + maAddr, err := ma.Address() + require.NoError(t, err) + + lsig, err := MakeLogicSig(program, args, sk1, ma) + require.NoError(t, err) + + err = AppendMultisigToLogicSig(&lsig, sk2) + require.NoError(t, err) + + t.Run("sender is contract addr", func(t *testing.T) { + expectedBytes := []byte{0x82, 0xa4, 0x6c, 0x73, 0x69, 0x67, 0x83, 0xa3, 0x61, 0x72, 0x67, 0x92, 0xc4, 0x1, 0x1, 0xc4, 0x2, 0x2, 0x3, 0xa1, 0x6c, 0xc4, 0x5, 0x1, 0x20, 0x1, 0x1, 0x22, 0xa4, 0x6d, 0x73, 0x69, 0x67, 0x83, 0xa6, 0x73, 0x75, 0x62, 0x73, 0x69, 0x67, 0x93, 0x82, 0xa2, 0x70, 0x6b, 0xc4, 0x20, 0x1b, 0x7e, 0xc0, 0xb0, 0x4b, 0xea, 0x61, 0xb7, 0x96, 0x90, 0x97, 0xe6, 0xcb, 0xf4, 0x7, 0xe1, 0x8, 0xa7, 0x5, 0x35, 0x1d, 0xb, 0xc9, 0x8a, 0xbe, 0xb1, 0x22, 0x9, 0xa8, 0xab, 0x81, 0x78, 0xa1, 0x73, 0xc4, 0x40, 0x49, 0x13, 0xb8, 0x5, 0xd1, 0x9e, 0x7f, 0x2c, 0x10, 0x80, 0xf6, 0x33, 0x7e, 0x18, 0x54, 0xa7, 0xce, 0xea, 0xee, 0x10, 0xdd, 0xbd, 0x13, 0x65, 0x84, 0xbf, 0x93, 0xb7, 0x5f, 0x30, 0x63, 0x15, 0x91, 0xca, 0x23, 0xc, 0xed, 0xef, 0x23, 0xd1, 0x74, 0x1b, 0x52, 0x9d, 0xb0, 0xff, 0xef, 0x37, 0x54, 0xd6, 0x46, 0xf4, 0xb5, 0x61, 0xfc, 0x8b, 0xbc, 0x2d, 0x7b, 0x4e, 0x63, 0x5c, 0xbd, 0x2, 0x82, 0xa2, 0x70, 0x6b, 0xc4, 0x20, 0x9, 0x63, 0x32, 0x9, 0x53, 0x73, 0x89, 0xf0, 0x75, 0x67, 0x11, 0x77, 0x39, 0x91, 0xc7, 0xd0, 0x3e, 0x1b, 0x73, 0xc8, 0xc4, 0xf5, 0x2b, 0xf6, 0xaf, 0xf0, 0x1a, 0xa2, 0x5c, 0xf9, 0xc2, 0x71, 0xa1, 0x73, 0xc4, 0x40, 0x64, 0xbc, 0x55, 0xdb, 0xed, 0x91, 0xa2, 0x41, 0xd4, 0x2a, 0xb6, 0x60, 0xf7, 0xe1, 0x4a, 0xb9, 0x99, 0x9a, 0x52, 0xb3, 0xb1, 0x71, 0x58, 0xce, 0xfc, 0x3f, 0x4f, 0xe7, 0xcb, 0x22, 0x41, 0x14, 0xad, 0xa9, 0x3d, 0x5e, 0x84, 0x5, 0x2, 0xa, 0x17, 0xa6, 0x69, 0x83, 0x3, 0x22, 0x4e, 0x86, 0xa3, 0x8b, 0x6a, 0x36, 0xc5, 0x54, 0xbe, 0x20, 0x50, 0xff, 0xd3, 0xee, 0xa8, 0xb3, 0x4, 0x9, 0x81, 0xa2, 0x70, 0x6b, 0xc4, 0x20, 0xe7, 0xf0, 0xf8, 0x4d, 0x6, 0x81, 0x1d, 0xf9, 0xf3, 0x1c, 0x8d, 0x87, 0x8b, 0x11, 0x55, 0xf4, 0x67, 0x1d, 0x51, 0xa1, 0x85, 0xc2, 0x0, 0x90, 0x86, 0x67, 0xf4, 0x49, 0x58, 0x70, 0x68, 0xa1, 0xa3, 0x74, 0x68, 0x72, 0x2, 0xa1, 0x76, 0x1, 0xa3, 0x74, 0x78, 0x6e, 0x89, 0xa3, 0x61, 0x6d, 0x74, 0xcd, 0x13, 0x88, 0xa3, 0x66, 0x65, 0x65, 0xce, 0x0, 0x3, 0x4f, 0xa8, 0xa2, 0x66, 0x76, 0xce, 0x0, 0xe, 0xd6, 0xdc, 0xa3, 0x67, 0x65, 0x6e, 0xad, 0x74, 0x65, 0x73, 0x74, 0x6e, 0x65, 0x74, 0x2d, 0x76, 0x33, 0x31, 0x2e, 0x30, 0xa2, 0x6c, 0x76, 0xce, 0x0, 0xe, 0xda, 0xc4, 0xa4, 0x6e, 0x6f, 0x74, 0x65, 0xc4, 0x8, 0xb4, 0x51, 0x79, 0x39, 0xfc, 0xfa, 0xd2, 0x71, 0xa3, 0x72, 0x63, 0x76, 0xc4, 0x20, 0xb4, 0xc6, 0x22, 0x68, 0xa4, 0xc3, 0x35, 0x50, 0xf5, 0x8a, 0x6d, 0xd4, 0xb8, 0xca, 0x23, 0x9c, 0x9f, 0x1d, 0x10, 0xb3, 0x41, 0xaa, 0x7e, 0x1a, 0x36, 0xe3, 0xce, 0x61, 0xfb, 0xfc, 0xf9, 0x8a, 0xa3, 0x73, 0x6e, 0x64, 0xc4, 0x20, 0x8d, 0x92, 0xb4, 0x89, 0x90, 0x1, 0x73, 0xa0, 0x4d, 0xfa, 0x43, 0x59, 0xa3, 0x66, 0x6a, 0x6a, 0xfc, 0xea, 0x2c, 0x42, 0xa0, 0x5d, 0xd9, 0xc1, 0xf7, 0x3e, 0xeb, 0xa5, 0x47, 0x80, 0x37, 0xe9, 0xa4, 0x74, 0x79, 0x70, 0x65, 0xa3, 0x70, 0x61, 0x79} + expectedTxid := "2I2QT3MGXNMB5IOTNWEQZWUJCHLJ5QFBYI264X5NWMUDKZXPZ5RA" + expectedAuthAddr := types.Address{} + sender := maAddr + testSign(t, lsig, sender, expectedBytes, expectedTxid, expectedAuthAddr) + }) + + t.Run("sender is not contract addr", func(t *testing.T) { + expectedBytes := []byte{0x83, 0xa4, 0x6c, 0x73, 0x69, 0x67, 0x83, 0xa3, 0x61, 0x72, 0x67, 0x92, 0xc4, 0x1, 0x1, 0xc4, 0x2, 0x2, 0x3, 0xa1, 0x6c, 0xc4, 0x5, 0x1, 0x20, 0x1, 0x1, 0x22, 0xa4, 0x6d, 0x73, 0x69, 0x67, 0x83, 0xa6, 0x73, 0x75, 0x62, 0x73, 0x69, 0x67, 0x93, 0x82, 0xa2, 0x70, 0x6b, 0xc4, 0x20, 0x1b, 0x7e, 0xc0, 0xb0, 0x4b, 0xea, 0x61, 0xb7, 0x96, 0x90, 0x97, 0xe6, 0xcb, 0xf4, 0x7, 0xe1, 0x8, 0xa7, 0x5, 0x35, 0x1d, 0xb, 0xc9, 0x8a, 0xbe, 0xb1, 0x22, 0x9, 0xa8, 0xab, 0x81, 0x78, 0xa1, 0x73, 0xc4, 0x40, 0x49, 0x13, 0xb8, 0x5, 0xd1, 0x9e, 0x7f, 0x2c, 0x10, 0x80, 0xf6, 0x33, 0x7e, 0x18, 0x54, 0xa7, 0xce, 0xea, 0xee, 0x10, 0xdd, 0xbd, 0x13, 0x65, 0x84, 0xbf, 0x93, 0xb7, 0x5f, 0x30, 0x63, 0x15, 0x91, 0xca, 0x23, 0xc, 0xed, 0xef, 0x23, 0xd1, 0x74, 0x1b, 0x52, 0x9d, 0xb0, 0xff, 0xef, 0x37, 0x54, 0xd6, 0x46, 0xf4, 0xb5, 0x61, 0xfc, 0x8b, 0xbc, 0x2d, 0x7b, 0x4e, 0x63, 0x5c, 0xbd, 0x2, 0x82, 0xa2, 0x70, 0x6b, 0xc4, 0x20, 0x9, 0x63, 0x32, 0x9, 0x53, 0x73, 0x89, 0xf0, 0x75, 0x67, 0x11, 0x77, 0x39, 0x91, 0xc7, 0xd0, 0x3e, 0x1b, 0x73, 0xc8, 0xc4, 0xf5, 0x2b, 0xf6, 0xaf, 0xf0, 0x1a, 0xa2, 0x5c, 0xf9, 0xc2, 0x71, 0xa1, 0x73, 0xc4, 0x40, 0x64, 0xbc, 0x55, 0xdb, 0xed, 0x91, 0xa2, 0x41, 0xd4, 0x2a, 0xb6, 0x60, 0xf7, 0xe1, 0x4a, 0xb9, 0x99, 0x9a, 0x52, 0xb3, 0xb1, 0x71, 0x58, 0xce, 0xfc, 0x3f, 0x4f, 0xe7, 0xcb, 0x22, 0x41, 0x14, 0xad, 0xa9, 0x3d, 0x5e, 0x84, 0x5, 0x2, 0xa, 0x17, 0xa6, 0x69, 0x83, 0x3, 0x22, 0x4e, 0x86, 0xa3, 0x8b, 0x6a, 0x36, 0xc5, 0x54, 0xbe, 0x20, 0x50, 0xff, 0xd3, 0xee, 0xa8, 0xb3, 0x4, 0x9, 0x81, 0xa2, 0x70, 0x6b, 0xc4, 0x20, 0xe7, 0xf0, 0xf8, 0x4d, 0x6, 0x81, 0x1d, 0xf9, 0xf3, 0x1c, 0x8d, 0x87, 0x8b, 0x11, 0x55, 0xf4, 0x67, 0x1d, 0x51, 0xa1, 0x85, 0xc2, 0x0, 0x90, 0x86, 0x67, 0xf4, 0x49, 0x58, 0x70, 0x68, 0xa1, 0xa3, 0x74, 0x68, 0x72, 0x2, 0xa1, 0x76, 0x1, 0xa4, 0x73, 0x67, 0x6e, 0x72, 0xc4, 0x20, 0x8d, 0x92, 0xb4, 0x89, 0x90, 0x1, 0x73, 0xa0, 0x4d, 0xfa, 0x43, 0x59, 0xa3, 0x66, 0x6a, 0x6a, 0xfc, 0xea, 0x2c, 0x42, 0xa0, 0x5d, 0xd9, 0xc1, 0xf7, 0x3e, 0xeb, 0xa5, 0x47, 0x80, 0x37, 0xe9, 0xa3, 0x74, 0x78, 0x6e, 0x89, 0xa3, 0x61, 0x6d, 0x74, 0xcd, 0x13, 0x88, 0xa3, 0x66, 0x65, 0x65, 0xce, 0x0, 0x3, 0x4f, 0xa8, 0xa2, 0x66, 0x76, 0xce, 0x0, 0xe, 0xd6, 0xdc, 0xa3, 0x67, 0x65, 0x6e, 0xad, 0x74, 0x65, 0x73, 0x74, 0x6e, 0x65, 0x74, 0x2d, 0x76, 0x33, 0x31, 0x2e, 0x30, 0xa2, 0x6c, 0x76, 0xce, 0x0, 0xe, 0xda, 0xc4, 0xa4, 0x6e, 0x6f, 0x74, 0x65, 0xc4, 0x8, 0xb4, 0x51, 0x79, 0x39, 0xfc, 0xfa, 0xd2, 0x71, 0xa3, 0x72, 0x63, 0x76, 0xc4, 0x20, 0xb4, 0xc6, 0x22, 0x68, 0xa4, 0xc3, 0x35, 0x50, 0xf5, 0x8a, 0x6d, 0xd4, 0xb8, 0xca, 0x23, 0x9c, 0x9f, 0x1d, 0x10, 0xb3, 0x41, 0xaa, 0x7e, 0x1a, 0x36, 0xe3, 0xce, 0x61, 0xfb, 0xfc, 0xf9, 0x8a, 0xa3, 0x73, 0x6e, 0x64, 0xc4, 0x20, 0xb4, 0xc6, 0x22, 0x68, 0xa4, 0xc3, 0x35, 0x50, 0xf5, 0x8a, 0x6d, 0xd4, 0xb8, 0xca, 0x23, 0x9c, 0x9f, 0x1d, 0x10, 0xb3, 0x41, 0xaa, 0x7e, 0x1a, 0x36, 0xe3, 0xce, 0x61, 0xfb, 0xfc, 0xf9, 0x8a, 0xa4, 0x74, 0x79, 0x70, 0x65, 0xa3, 0x70, 0x61, 0x79} + expectedTxid := "U4X24Q45MCZ6JSL343QNR3RC6AJO2NUDQXFCTNONIW3SU2AYQJOA" + expectedAuthAddr := maAddr + sender := otherAddr + testSign(t, lsig, sender, expectedBytes, expectedTxid, expectedAuthAddr) + }) + }) +} + +func TestSignLogicSigAccountTransaction(t *testing.T) { + program := []byte{1, 32, 1, 1, 34} + args := [][]byte{ + {0x01}, + {0x02, 0x03}, + } + + otherAddrStr := "WTDCE2FEYM2VB5MKNXKLRSRDTSPR2EFTIGVH4GRW4PHGD6747GFJTBGT2A" + otherAddr, err := types.DecodeAddress(otherAddrStr) + require.NoError(t, err) + + testSign := func(t *testing.T, lsa LogicSigAccount, sender types.Address, expectedBytes []byte, expectedTxid string, expectedAuthAddr types.Address) { + txn := types.Transaction{ + Type: types.PaymentTx, + Header: types.Header{ + Sender: sender, + Fee: 217000, + FirstValid: 972508, + LastValid: 973508, + Note: []byte{180, 81, 121, 57, 252, 250, 210, 113}, + GenesisID: "testnet-v31.0", + }, + PaymentTxnFields: types.PaymentTxnFields{ + Receiver: otherAddr, + Amount: 5000, + }, + } + + txid, stxnBytes, err := SignLogicSigAccountTransaction(lsa, txn) + require.NoError(t, err) + require.EqualValues(t, expectedBytes, stxnBytes) + require.Equal(t, expectedTxid, txid) + + // decode and verify + var stx types.SignedTxn + err = msgpack.Decode(stxnBytes, &stx) + require.NoError(t, err) + + require.Equal(t, expectedAuthAddr, stx.AuthAddr) + + require.Equal(t, lsa.Lsig, stx.Lsig) + require.Equal(t, types.Signature{}, stx.Sig) + require.True(t, stx.Msig.Blank()) + + require.Equal(t, types.Signature{}, stx.Sig) + require.True(t, stx.Msig.Blank()) + + require.Equal(t, txn, stx.Txn) + } + + t.Run("no sig", func(t *testing.T) { + lsigAccount := MakeLogicSigAccountEscrow(program, args) + + programAddr, err := types.DecodeAddress("6Z3C3LDVWGMX23BMSYMANACQOSINPFIRF77H7N3AWJZYV6OH6GWTJKVMXY") + require.NoError(t, err) + + t.Run("sender is contract addr", func(t *testing.T) { + expectedBytes := []byte{0x82, 0xa4, 0x6c, 0x73, 0x69, 0x67, 0x82, 0xa3, 0x61, 0x72, 0x67, 0x92, 0xc4, 0x1, 0x1, 0xc4, 0x2, 0x2, 0x3, 0xa1, 0x6c, 0xc4, 0x5, 0x1, 0x20, 0x1, 0x1, 0x22, 0xa3, 0x74, 0x78, 0x6e, 0x89, 0xa3, 0x61, 0x6d, 0x74, 0xcd, 0x13, 0x88, 0xa3, 0x66, 0x65, 0x65, 0xce, 0x0, 0x3, 0x4f, 0xa8, 0xa2, 0x66, 0x76, 0xce, 0x0, 0xe, 0xd6, 0xdc, 0xa3, 0x67, 0x65, 0x6e, 0xad, 0x74, 0x65, 0x73, 0x74, 0x6e, 0x65, 0x74, 0x2d, 0x76, 0x33, 0x31, 0x2e, 0x30, 0xa2, 0x6c, 0x76, 0xce, 0x0, 0xe, 0xda, 0xc4, 0xa4, 0x6e, 0x6f, 0x74, 0x65, 0xc4, 0x8, 0xb4, 0x51, 0x79, 0x39, 0xfc, 0xfa, 0xd2, 0x71, 0xa3, 0x72, 0x63, 0x76, 0xc4, 0x20, 0xb4, 0xc6, 0x22, 0x68, 0xa4, 0xc3, 0x35, 0x50, 0xf5, 0x8a, 0x6d, 0xd4, 0xb8, 0xca, 0x23, 0x9c, 0x9f, 0x1d, 0x10, 0xb3, 0x41, 0xaa, 0x7e, 0x1a, 0x36, 0xe3, 0xce, 0x61, 0xfb, 0xfc, 0xf9, 0x8a, 0xa3, 0x73, 0x6e, 0x64, 0xc4, 0x20, 0xf6, 0x76, 0x2d, 0xac, 0x75, 0xb1, 0x99, 0x7d, 0x6c, 0x2c, 0x96, 0x18, 0x6, 0x80, 0x50, 0x74, 0x90, 0xd7, 0x95, 0x11, 0x2f, 0xfe, 0x7f, 0xb7, 0x60, 0xb2, 0x73, 0x8a, 0xf9, 0xc7, 0xf1, 0xad, 0xa4, 0x74, 0x79, 0x70, 0x65, 0xa3, 0x70, 0x61, 0x79} + expectedTxid := "IL5UCKXGWBA2MQ4YYFQKYC3BFCWO2KHZSNZWVDIXOOZS3AWVIQDA" + expectedAuthAddr := types.Address{} + sender := programAddr + testSign(t, lsigAccount, sender, expectedBytes, expectedTxid, expectedAuthAddr) + }) + + t.Run("sender is not contract addr", func(t *testing.T) { + expectedBytes := []byte{0x83, 0xa4, 0x6c, 0x73, 0x69, 0x67, 0x82, 0xa3, 0x61, 0x72, 0x67, 0x92, 0xc4, 0x1, 0x1, 0xc4, 0x2, 0x2, 0x3, 0xa1, 0x6c, 0xc4, 0x5, 0x1, 0x20, 0x1, 0x1, 0x22, 0xa4, 0x73, 0x67, 0x6e, 0x72, 0xc4, 0x20, 0xf6, 0x76, 0x2d, 0xac, 0x75, 0xb1, 0x99, 0x7d, 0x6c, 0x2c, 0x96, 0x18, 0x6, 0x80, 0x50, 0x74, 0x90, 0xd7, 0x95, 0x11, 0x2f, 0xfe, 0x7f, 0xb7, 0x60, 0xb2, 0x73, 0x8a, 0xf9, 0xc7, 0xf1, 0xad, 0xa3, 0x74, 0x78, 0x6e, 0x89, 0xa3, 0x61, 0x6d, 0x74, 0xcd, 0x13, 0x88, 0xa3, 0x66, 0x65, 0x65, 0xce, 0x0, 0x3, 0x4f, 0xa8, 0xa2, 0x66, 0x76, 0xce, 0x0, 0xe, 0xd6, 0xdc, 0xa3, 0x67, 0x65, 0x6e, 0xad, 0x74, 0x65, 0x73, 0x74, 0x6e, 0x65, 0x74, 0x2d, 0x76, 0x33, 0x31, 0x2e, 0x30, 0xa2, 0x6c, 0x76, 0xce, 0x0, 0xe, 0xda, 0xc4, 0xa4, 0x6e, 0x6f, 0x74, 0x65, 0xc4, 0x8, 0xb4, 0x51, 0x79, 0x39, 0xfc, 0xfa, 0xd2, 0x71, 0xa3, 0x72, 0x63, 0x76, 0xc4, 0x20, 0xb4, 0xc6, 0x22, 0x68, 0xa4, 0xc3, 0x35, 0x50, 0xf5, 0x8a, 0x6d, 0xd4, 0xb8, 0xca, 0x23, 0x9c, 0x9f, 0x1d, 0x10, 0xb3, 0x41, 0xaa, 0x7e, 0x1a, 0x36, 0xe3, 0xce, 0x61, 0xfb, 0xfc, 0xf9, 0x8a, 0xa3, 0x73, 0x6e, 0x64, 0xc4, 0x20, 0xb4, 0xc6, 0x22, 0x68, 0xa4, 0xc3, 0x35, 0x50, 0xf5, 0x8a, 0x6d, 0xd4, 0xb8, 0xca, 0x23, 0x9c, 0x9f, 0x1d, 0x10, 0xb3, 0x41, 0xaa, 0x7e, 0x1a, 0x36, 0xe3, 0xce, 0x61, 0xfb, 0xfc, 0xf9, 0x8a, 0xa4, 0x74, 0x79, 0x70, 0x65, 0xa3, 0x70, 0x61, 0x79} + expectedTxid := "U4X24Q45MCZ6JSL343QNR3RC6AJO2NUDQXFCTNONIW3SU2AYQJOA" + expectedAuthAddr := programAddr + sender := otherAddr + testSign(t, lsigAccount, sender, expectedBytes, expectedTxid, expectedAuthAddr) + }) + }) + + t.Run("single sig", func(t *testing.T) { + account, err := AccountFromPrivateKey(ed25519.PrivateKey{0xd2, 0xdc, 0x4c, 0xcc, 0xe9, 0x98, 0x62, 0xff, 0xcf, 0x8c, 0xeb, 0x93, 0x6, 0xc4, 0x8d, 0xa6, 0x80, 0x50, 0x82, 0xa, 0xbb, 0x29, 0x95, 0x7a, 0xac, 0x82, 0x68, 0x9a, 0x8c, 0x49, 0x5a, 0x38, 0x5e, 0x67, 0x4f, 0x1c, 0xa, 0xee, 0xec, 0x37, 0x71, 0x89, 0x8f, 0x61, 0xc7, 0x6f, 0xf5, 0xd2, 0x4a, 0x19, 0x79, 0x3e, 0x2c, 0x91, 0xfa, 0x8, 0x51, 0x62, 0x63, 0xe3, 0x85, 0x73, 0xea, 0x42}) + require.NoError(t, err) + + lsigAccount, err := MakeLogicSigAccountDelegated(program, args, account.PrivateKey) + require.NoError(t, err) + + t.Run("sender is contract addr", func(t *testing.T) { + expectedBytes := []byte{0x82, 0xa4, 0x6c, 0x73, 0x69, 0x67, 0x83, 0xa3, 0x61, 0x72, 0x67, 0x92, 0xc4, 0x1, 0x1, 0xc4, 0x2, 0x2, 0x3, 0xa1, 0x6c, 0xc4, 0x5, 0x1, 0x20, 0x1, 0x1, 0x22, 0xa3, 0x73, 0x69, 0x67, 0xc4, 0x40, 0x3e, 0x5, 0x3d, 0x39, 0x4d, 0xfb, 0x12, 0xbc, 0x65, 0x79, 0x9f, 0xea, 0x31, 0x8a, 0x7b, 0x8e, 0xa2, 0x51, 0x8b, 0x55, 0x2c, 0x8a, 0xbe, 0x6c, 0xd7, 0xa7, 0x65, 0x2d, 0xd8, 0xb0, 0x18, 0x7e, 0x21, 0x5, 0x2d, 0xb9, 0x24, 0x62, 0x89, 0x16, 0xe5, 0x61, 0x74, 0xcd, 0xf, 0x19, 0xac, 0xb9, 0x6c, 0x45, 0xa4, 0x29, 0x91, 0x99, 0x11, 0x1d, 0xe4, 0x7c, 0xe4, 0xfc, 0x12, 0xec, 0xce, 0x2, 0xa3, 0x74, 0x78, 0x6e, 0x89, 0xa3, 0x61, 0x6d, 0x74, 0xcd, 0x13, 0x88, 0xa3, 0x66, 0x65, 0x65, 0xce, 0x0, 0x3, 0x4f, 0xa8, 0xa2, 0x66, 0x76, 0xce, 0x0, 0xe, 0xd6, 0xdc, 0xa3, 0x67, 0x65, 0x6e, 0xad, 0x74, 0x65, 0x73, 0x74, 0x6e, 0x65, 0x74, 0x2d, 0x76, 0x33, 0x31, 0x2e, 0x30, 0xa2, 0x6c, 0x76, 0xce, 0x0, 0xe, 0xda, 0xc4, 0xa4, 0x6e, 0x6f, 0x74, 0x65, 0xc4, 0x8, 0xb4, 0x51, 0x79, 0x39, 0xfc, 0xfa, 0xd2, 0x71, 0xa3, 0x72, 0x63, 0x76, 0xc4, 0x20, 0xb4, 0xc6, 0x22, 0x68, 0xa4, 0xc3, 0x35, 0x50, 0xf5, 0x8a, 0x6d, 0xd4, 0xb8, 0xca, 0x23, 0x9c, 0x9f, 0x1d, 0x10, 0xb3, 0x41, 0xaa, 0x7e, 0x1a, 0x36, 0xe3, 0xce, 0x61, 0xfb, 0xfc, 0xf9, 0x8a, 0xa3, 0x73, 0x6e, 0x64, 0xc4, 0x20, 0x5e, 0x67, 0x4f, 0x1c, 0xa, 0xee, 0xec, 0x37, 0x71, 0x89, 0x8f, 0x61, 0xc7, 0x6f, 0xf5, 0xd2, 0x4a, 0x19, 0x79, 0x3e, 0x2c, 0x91, 0xfa, 0x8, 0x51, 0x62, 0x63, 0xe3, 0x85, 0x73, 0xea, 0x42, 0xa4, 0x74, 0x79, 0x70, 0x65, 0xa3, 0x70, 0x61, 0x79} + expectedTxid := "XPFTYDOV5RCL7K5EGTC32PSTRKFO3ITZIL64CRTYIJEPHXOTCCXA" + expectedAuthAddr := types.Address{} + sender := account.Address + testSign(t, lsigAccount, sender, expectedBytes, expectedTxid, expectedAuthAddr) + }) + + t.Run("sender is not contract addr", func(t *testing.T) { + expectedBytes := []byte{0x83, 0xa4, 0x6c, 0x73, 0x69, 0x67, 0x83, 0xa3, 0x61, 0x72, 0x67, 0x92, 0xc4, 0x1, 0x1, 0xc4, 0x2, 0x2, 0x3, 0xa1, 0x6c, 0xc4, 0x5, 0x1, 0x20, 0x1, 0x1, 0x22, 0xa3, 0x73, 0x69, 0x67, 0xc4, 0x40, 0x3e, 0x5, 0x3d, 0x39, 0x4d, 0xfb, 0x12, 0xbc, 0x65, 0x79, 0x9f, 0xea, 0x31, 0x8a, 0x7b, 0x8e, 0xa2, 0x51, 0x8b, 0x55, 0x2c, 0x8a, 0xbe, 0x6c, 0xd7, 0xa7, 0x65, 0x2d, 0xd8, 0xb0, 0x18, 0x7e, 0x21, 0x5, 0x2d, 0xb9, 0x24, 0x62, 0x89, 0x16, 0xe5, 0x61, 0x74, 0xcd, 0xf, 0x19, 0xac, 0xb9, 0x6c, 0x45, 0xa4, 0x29, 0x91, 0x99, 0x11, 0x1d, 0xe4, 0x7c, 0xe4, 0xfc, 0x12, 0xec, 0xce, 0x2, 0xa4, 0x73, 0x67, 0x6e, 0x72, 0xc4, 0x20, 0x5e, 0x67, 0x4f, 0x1c, 0xa, 0xee, 0xec, 0x37, 0x71, 0x89, 0x8f, 0x61, 0xc7, 0x6f, 0xf5, 0xd2, 0x4a, 0x19, 0x79, 0x3e, 0x2c, 0x91, 0xfa, 0x8, 0x51, 0x62, 0x63, 0xe3, 0x85, 0x73, 0xea, 0x42, 0xa3, 0x74, 0x78, 0x6e, 0x89, 0xa3, 0x61, 0x6d, 0x74, 0xcd, 0x13, 0x88, 0xa3, 0x66, 0x65, 0x65, 0xce, 0x0, 0x3, 0x4f, 0xa8, 0xa2, 0x66, 0x76, 0xce, 0x0, 0xe, 0xd6, 0xdc, 0xa3, 0x67, 0x65, 0x6e, 0xad, 0x74, 0x65, 0x73, 0x74, 0x6e, 0x65, 0x74, 0x2d, 0x76, 0x33, 0x31, 0x2e, 0x30, 0xa2, 0x6c, 0x76, 0xce, 0x0, 0xe, 0xda, 0xc4, 0xa4, 0x6e, 0x6f, 0x74, 0x65, 0xc4, 0x8, 0xb4, 0x51, 0x79, 0x39, 0xfc, 0xfa, 0xd2, 0x71, 0xa3, 0x72, 0x63, 0x76, 0xc4, 0x20, 0xb4, 0xc6, 0x22, 0x68, 0xa4, 0xc3, 0x35, 0x50, 0xf5, 0x8a, 0x6d, 0xd4, 0xb8, 0xca, 0x23, 0x9c, 0x9f, 0x1d, 0x10, 0xb3, 0x41, 0xaa, 0x7e, 0x1a, 0x36, 0xe3, 0xce, 0x61, 0xfb, 0xfc, 0xf9, 0x8a, 0xa3, 0x73, 0x6e, 0x64, 0xc4, 0x20, 0xb4, 0xc6, 0x22, 0x68, 0xa4, 0xc3, 0x35, 0x50, 0xf5, 0x8a, 0x6d, 0xd4, 0xb8, 0xca, 0x23, 0x9c, 0x9f, 0x1d, 0x10, 0xb3, 0x41, 0xaa, 0x7e, 0x1a, 0x36, 0xe3, 0xce, 0x61, 0xfb, 0xfc, 0xf9, 0x8a, 0xa4, 0x74, 0x79, 0x70, 0x65, 0xa3, 0x70, 0x61, 0x79} + expectedTxid := "U4X24Q45MCZ6JSL343QNR3RC6AJO2NUDQXFCTNONIW3SU2AYQJOA" + expectedAuthAddr := account.Address + sender := otherAddr + testSign(t, lsigAccount, sender, expectedBytes, expectedTxid, expectedAuthAddr) + }) + }) + + t.Run("multi sig", func(t *testing.T) { + ma, sk1, sk2, _ := makeTestMultisigAccount(t) + maAddr, err := ma.Address() + require.NoError(t, err) + + lsigAccount, err := MakeLogicSigAccountDelegatedMsig(program, args, ma, sk1) + require.NoError(t, err) + + err = lsigAccount.AppendMultisigSignature(sk2) + require.NoError(t, err) + + t.Run("sender is contract addr", func(t *testing.T) { + expectedBytes := []byte{0x82, 0xa4, 0x6c, 0x73, 0x69, 0x67, 0x83, 0xa3, 0x61, 0x72, 0x67, 0x92, 0xc4, 0x1, 0x1, 0xc4, 0x2, 0x2, 0x3, 0xa1, 0x6c, 0xc4, 0x5, 0x1, 0x20, 0x1, 0x1, 0x22, 0xa4, 0x6d, 0x73, 0x69, 0x67, 0x83, 0xa6, 0x73, 0x75, 0x62, 0x73, 0x69, 0x67, 0x93, 0x82, 0xa2, 0x70, 0x6b, 0xc4, 0x20, 0x1b, 0x7e, 0xc0, 0xb0, 0x4b, 0xea, 0x61, 0xb7, 0x96, 0x90, 0x97, 0xe6, 0xcb, 0xf4, 0x7, 0xe1, 0x8, 0xa7, 0x5, 0x35, 0x1d, 0xb, 0xc9, 0x8a, 0xbe, 0xb1, 0x22, 0x9, 0xa8, 0xab, 0x81, 0x78, 0xa1, 0x73, 0xc4, 0x40, 0x49, 0x13, 0xb8, 0x5, 0xd1, 0x9e, 0x7f, 0x2c, 0x10, 0x80, 0xf6, 0x33, 0x7e, 0x18, 0x54, 0xa7, 0xce, 0xea, 0xee, 0x10, 0xdd, 0xbd, 0x13, 0x65, 0x84, 0xbf, 0x93, 0xb7, 0x5f, 0x30, 0x63, 0x15, 0x91, 0xca, 0x23, 0xc, 0xed, 0xef, 0x23, 0xd1, 0x74, 0x1b, 0x52, 0x9d, 0xb0, 0xff, 0xef, 0x37, 0x54, 0xd6, 0x46, 0xf4, 0xb5, 0x61, 0xfc, 0x8b, 0xbc, 0x2d, 0x7b, 0x4e, 0x63, 0x5c, 0xbd, 0x2, 0x82, 0xa2, 0x70, 0x6b, 0xc4, 0x20, 0x9, 0x63, 0x32, 0x9, 0x53, 0x73, 0x89, 0xf0, 0x75, 0x67, 0x11, 0x77, 0x39, 0x91, 0xc7, 0xd0, 0x3e, 0x1b, 0x73, 0xc8, 0xc4, 0xf5, 0x2b, 0xf6, 0xaf, 0xf0, 0x1a, 0xa2, 0x5c, 0xf9, 0xc2, 0x71, 0xa1, 0x73, 0xc4, 0x40, 0x64, 0xbc, 0x55, 0xdb, 0xed, 0x91, 0xa2, 0x41, 0xd4, 0x2a, 0xb6, 0x60, 0xf7, 0xe1, 0x4a, 0xb9, 0x99, 0x9a, 0x52, 0xb3, 0xb1, 0x71, 0x58, 0xce, 0xfc, 0x3f, 0x4f, 0xe7, 0xcb, 0x22, 0x41, 0x14, 0xad, 0xa9, 0x3d, 0x5e, 0x84, 0x5, 0x2, 0xa, 0x17, 0xa6, 0x69, 0x83, 0x3, 0x22, 0x4e, 0x86, 0xa3, 0x8b, 0x6a, 0x36, 0xc5, 0x54, 0xbe, 0x20, 0x50, 0xff, 0xd3, 0xee, 0xa8, 0xb3, 0x4, 0x9, 0x81, 0xa2, 0x70, 0x6b, 0xc4, 0x20, 0xe7, 0xf0, 0xf8, 0x4d, 0x6, 0x81, 0x1d, 0xf9, 0xf3, 0x1c, 0x8d, 0x87, 0x8b, 0x11, 0x55, 0xf4, 0x67, 0x1d, 0x51, 0xa1, 0x85, 0xc2, 0x0, 0x90, 0x86, 0x67, 0xf4, 0x49, 0x58, 0x70, 0x68, 0xa1, 0xa3, 0x74, 0x68, 0x72, 0x2, 0xa1, 0x76, 0x1, 0xa3, 0x74, 0x78, 0x6e, 0x89, 0xa3, 0x61, 0x6d, 0x74, 0xcd, 0x13, 0x88, 0xa3, 0x66, 0x65, 0x65, 0xce, 0x0, 0x3, 0x4f, 0xa8, 0xa2, 0x66, 0x76, 0xce, 0x0, 0xe, 0xd6, 0xdc, 0xa3, 0x67, 0x65, 0x6e, 0xad, 0x74, 0x65, 0x73, 0x74, 0x6e, 0x65, 0x74, 0x2d, 0x76, 0x33, 0x31, 0x2e, 0x30, 0xa2, 0x6c, 0x76, 0xce, 0x0, 0xe, 0xda, 0xc4, 0xa4, 0x6e, 0x6f, 0x74, 0x65, 0xc4, 0x8, 0xb4, 0x51, 0x79, 0x39, 0xfc, 0xfa, 0xd2, 0x71, 0xa3, 0x72, 0x63, 0x76, 0xc4, 0x20, 0xb4, 0xc6, 0x22, 0x68, 0xa4, 0xc3, 0x35, 0x50, 0xf5, 0x8a, 0x6d, 0xd4, 0xb8, 0xca, 0x23, 0x9c, 0x9f, 0x1d, 0x10, 0xb3, 0x41, 0xaa, 0x7e, 0x1a, 0x36, 0xe3, 0xce, 0x61, 0xfb, 0xfc, 0xf9, 0x8a, 0xa3, 0x73, 0x6e, 0x64, 0xc4, 0x20, 0x8d, 0x92, 0xb4, 0x89, 0x90, 0x1, 0x73, 0xa0, 0x4d, 0xfa, 0x43, 0x59, 0xa3, 0x66, 0x6a, 0x6a, 0xfc, 0xea, 0x2c, 0x42, 0xa0, 0x5d, 0xd9, 0xc1, 0xf7, 0x3e, 0xeb, 0xa5, 0x47, 0x80, 0x37, 0xe9, 0xa4, 0x74, 0x79, 0x70, 0x65, 0xa3, 0x70, 0x61, 0x79} + expectedTxid := "2I2QT3MGXNMB5IOTNWEQZWUJCHLJ5QFBYI264X5NWMUDKZXPZ5RA" + expectedAuthAddr := types.Address{} + sender := maAddr + testSign(t, lsigAccount, sender, expectedBytes, expectedTxid, expectedAuthAddr) + }) + + t.Run("sender is not contract addr", func(t *testing.T) { + expectedBytes := []byte{0x83, 0xa4, 0x6c, 0x73, 0x69, 0x67, 0x83, 0xa3, 0x61, 0x72, 0x67, 0x92, 0xc4, 0x1, 0x1, 0xc4, 0x2, 0x2, 0x3, 0xa1, 0x6c, 0xc4, 0x5, 0x1, 0x20, 0x1, 0x1, 0x22, 0xa4, 0x6d, 0x73, 0x69, 0x67, 0x83, 0xa6, 0x73, 0x75, 0x62, 0x73, 0x69, 0x67, 0x93, 0x82, 0xa2, 0x70, 0x6b, 0xc4, 0x20, 0x1b, 0x7e, 0xc0, 0xb0, 0x4b, 0xea, 0x61, 0xb7, 0x96, 0x90, 0x97, 0xe6, 0xcb, 0xf4, 0x7, 0xe1, 0x8, 0xa7, 0x5, 0x35, 0x1d, 0xb, 0xc9, 0x8a, 0xbe, 0xb1, 0x22, 0x9, 0xa8, 0xab, 0x81, 0x78, 0xa1, 0x73, 0xc4, 0x40, 0x49, 0x13, 0xb8, 0x5, 0xd1, 0x9e, 0x7f, 0x2c, 0x10, 0x80, 0xf6, 0x33, 0x7e, 0x18, 0x54, 0xa7, 0xce, 0xea, 0xee, 0x10, 0xdd, 0xbd, 0x13, 0x65, 0x84, 0xbf, 0x93, 0xb7, 0x5f, 0x30, 0x63, 0x15, 0x91, 0xca, 0x23, 0xc, 0xed, 0xef, 0x23, 0xd1, 0x74, 0x1b, 0x52, 0x9d, 0xb0, 0xff, 0xef, 0x37, 0x54, 0xd6, 0x46, 0xf4, 0xb5, 0x61, 0xfc, 0x8b, 0xbc, 0x2d, 0x7b, 0x4e, 0x63, 0x5c, 0xbd, 0x2, 0x82, 0xa2, 0x70, 0x6b, 0xc4, 0x20, 0x9, 0x63, 0x32, 0x9, 0x53, 0x73, 0x89, 0xf0, 0x75, 0x67, 0x11, 0x77, 0x39, 0x91, 0xc7, 0xd0, 0x3e, 0x1b, 0x73, 0xc8, 0xc4, 0xf5, 0x2b, 0xf6, 0xaf, 0xf0, 0x1a, 0xa2, 0x5c, 0xf9, 0xc2, 0x71, 0xa1, 0x73, 0xc4, 0x40, 0x64, 0xbc, 0x55, 0xdb, 0xed, 0x91, 0xa2, 0x41, 0xd4, 0x2a, 0xb6, 0x60, 0xf7, 0xe1, 0x4a, 0xb9, 0x99, 0x9a, 0x52, 0xb3, 0xb1, 0x71, 0x58, 0xce, 0xfc, 0x3f, 0x4f, 0xe7, 0xcb, 0x22, 0x41, 0x14, 0xad, 0xa9, 0x3d, 0x5e, 0x84, 0x5, 0x2, 0xa, 0x17, 0xa6, 0x69, 0x83, 0x3, 0x22, 0x4e, 0x86, 0xa3, 0x8b, 0x6a, 0x36, 0xc5, 0x54, 0xbe, 0x20, 0x50, 0xff, 0xd3, 0xee, 0xa8, 0xb3, 0x4, 0x9, 0x81, 0xa2, 0x70, 0x6b, 0xc4, 0x20, 0xe7, 0xf0, 0xf8, 0x4d, 0x6, 0x81, 0x1d, 0xf9, 0xf3, 0x1c, 0x8d, 0x87, 0x8b, 0x11, 0x55, 0xf4, 0x67, 0x1d, 0x51, 0xa1, 0x85, 0xc2, 0x0, 0x90, 0x86, 0x67, 0xf4, 0x49, 0x58, 0x70, 0x68, 0xa1, 0xa3, 0x74, 0x68, 0x72, 0x2, 0xa1, 0x76, 0x1, 0xa4, 0x73, 0x67, 0x6e, 0x72, 0xc4, 0x20, 0x8d, 0x92, 0xb4, 0x89, 0x90, 0x1, 0x73, 0xa0, 0x4d, 0xfa, 0x43, 0x59, 0xa3, 0x66, 0x6a, 0x6a, 0xfc, 0xea, 0x2c, 0x42, 0xa0, 0x5d, 0xd9, 0xc1, 0xf7, 0x3e, 0xeb, 0xa5, 0x47, 0x80, 0x37, 0xe9, 0xa3, 0x74, 0x78, 0x6e, 0x89, 0xa3, 0x61, 0x6d, 0x74, 0xcd, 0x13, 0x88, 0xa3, 0x66, 0x65, 0x65, 0xce, 0x0, 0x3, 0x4f, 0xa8, 0xa2, 0x66, 0x76, 0xce, 0x0, 0xe, 0xd6, 0xdc, 0xa3, 0x67, 0x65, 0x6e, 0xad, 0x74, 0x65, 0x73, 0x74, 0x6e, 0x65, 0x74, 0x2d, 0x76, 0x33, 0x31, 0x2e, 0x30, 0xa2, 0x6c, 0x76, 0xce, 0x0, 0xe, 0xda, 0xc4, 0xa4, 0x6e, 0x6f, 0x74, 0x65, 0xc4, 0x8, 0xb4, 0x51, 0x79, 0x39, 0xfc, 0xfa, 0xd2, 0x71, 0xa3, 0x72, 0x63, 0x76, 0xc4, 0x20, 0xb4, 0xc6, 0x22, 0x68, 0xa4, 0xc3, 0x35, 0x50, 0xf5, 0x8a, 0x6d, 0xd4, 0xb8, 0xca, 0x23, 0x9c, 0x9f, 0x1d, 0x10, 0xb3, 0x41, 0xaa, 0x7e, 0x1a, 0x36, 0xe3, 0xce, 0x61, 0xfb, 0xfc, 0xf9, 0x8a, 0xa3, 0x73, 0x6e, 0x64, 0xc4, 0x20, 0xb4, 0xc6, 0x22, 0x68, 0xa4, 0xc3, 0x35, 0x50, 0xf5, 0x8a, 0x6d, 0xd4, 0xb8, 0xca, 0x23, 0x9c, 0x9f, 0x1d, 0x10, 0xb3, 0x41, 0xaa, 0x7e, 0x1a, 0x36, 0xe3, 0xce, 0x61, 0xfb, 0xfc, 0xf9, 0x8a, 0xa4, 0x74, 0x79, 0x70, 0x65, 0xa3, 0x70, 0x61, 0x79} + expectedTxid := "U4X24Q45MCZ6JSL343QNR3RC6AJO2NUDQXFCTNONIW3SU2AYQJOA" + expectedAuthAddr := maAddr + sender := otherAddr + testSign(t, lsigAccount, sender, expectedBytes, expectedTxid, expectedAuthAddr) + }) + }) +} + func TestTealSign(t *testing.T) { data, err := base64.StdEncoding.DecodeString("Ux8jntyBJQarjKGF8A==") require.NoError(t, err) diff --git a/crypto/errors.go b/crypto/errors.go index 150c44cf..0e20d8b4 100644 --- a/crypto/errors.go +++ b/crypto/errors.go @@ -5,13 +5,19 @@ import ( ) var errInvalidSignatureReturned = errors.New("ed25519 library returned an invalid signature") +var errInvalidPrivateKey = errors.New("invalid private key") var errMsigUnknownVersion = errors.New("unknown version != 1") var errMsigInvalidThreshold = errors.New("invalid threshold") -var errMsigBadTxnSender = errors.New("transaction sender does not match multisig parameters") var errMsigInvalidSecretKey = errors.New("secret key has no corresponding public identity in multisig preimage") var errMsigMergeLessThanTwo = errors.New("cannot merge fewer than two multisig transactions") var errMsigMergeKeysMismatch = errors.New("multisig parameters do not match") var errMsigMergeInvalidDups = errors.New("mismatched duplicate signatures") +var errMsigMergeAuthAddrMismatch = errors.New("mismatched AuthAddrs") +var errLsigTooManySignatures = errors.New("logicsig has too many signatures, at most one of Sig or Msig may be defined") +var errLsigNoSignature = errors.New("logicsig is not delegated") var errLsigInvalidSignature = errors.New("invalid logicsig signature") +var errLsigNoPublicKey = errors.New("missing public key of delegated logicsig") +var errLsigInvalidPublicKey = errors.New("public key does not match logicsig signature") var errLsigInvalidProgram = errors.New("invalid logicsig program") var errLsigEmptyMsig = errors.New("empty multisig in logicsig") +var errLsigAccountPublicKeyNotNeeded = errors.New("a public key for the signer was provided when none was expected") diff --git a/types/signature.go b/types/signature.go index 3ca84341..c51cbe17 100644 --- a/types/signature.go +++ b/types/signature.go @@ -50,7 +50,10 @@ type LogicSig struct { // OR hashed to be the Address of an account. Logic []byte `codec:"l"` - Sig Signature `codec:"sig"` + // The signature of the account that has delegated to this LogicSig, if any + Sig Signature `codec:"sig"` + + // The signature of the multisig account that has delegated to this LogicSig, if any Msig MultisigSig `codec:"msig"` // Args are not signed, but checked by Logic From 3629b9154f221db656f368c8b93a776fe4f5b130 Mon Sep 17 00:00:00 2001 From: Barbara Poon Date: Mon, 2 Aug 2021 15:07:42 -0400 Subject: [PATCH 5/6] Updated CHANGELOG.md --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 94b3f197..0ea6c7a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# 1.10.0 +# Added +- New github Issue template +- Signing support for rekeying to LogicSig/MultiSig account +# BugFix +- Use correct go version in CI +# BugFix # 1.9.2 # Bug Fix - Update FromBase64String() to correctly return the signed transaction From 6994fe904d231be7f2a6bc2615170b0e964d9da9 Mon Sep 17 00:00:00 2001 From: Barbara Poon Date: Mon, 2 Aug 2021 16:39:20 -0400 Subject: [PATCH 6/6] add asset base64 field to changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ea6c7a8..0741b6ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ # Added - New github Issue template - Signing support for rekeying to LogicSig/MultiSig account +- Asset Base64 Fields # BugFix - Use correct go version in CI # BugFix