package tests import ( "crypto/sha256" "encoding/hex" "kerma/models" "testing" "github.com/docker/go/canonical/json" . "github.com/smartystreets/goconvey/convey" ) func TestTransaction(t *testing.T) { Convey("Given a new coinbase transaction", t, func() { height := uint64(1) transaction := models.Transaction{ Type: "transaction", Height: &height, Outputs: []models.Output{ { Pubkey: "62b7c521cd9211579cf70fd4099315643767b96711febaa5c76dc3daf27c281c", Value: 50000000000000, }, }, } Convey("When GetType is called, 'transaction' should be returned", func() { So(transaction.GetType(), ShouldEqual, "transaction") }) Convey("When GetEntity is called, the transaction should be returned", func() { So(transaction.GetEntity(), ShouldResemble, &transaction) }) Convey("When Validate is called, no error should be returned", func() { So(transaction.Validate(), ShouldEqual, nil) }) Convey("When IsCoinbaseTx is called, 'true' should be returned", func() { So(transaction.IsCoinbaseTx(), ShouldEqual, true) }) Convey("When IsTxInput is called, 'false' should be returned", func() { So(transaction.IsTxInput("dasdfasfds"), ShouldEqual, false) }) Convey("When Hash is called, the hash of the cannonical json and no error should be returned", func() { transactionJSON, _ := json.MarshalCanonical(transaction) hashSum := sha256.Sum256(transactionJSON) res, err := transaction.Hash() So(hex.EncodeToString(hashSum[:]), ShouldEqual, res) So(err, ShouldEqual, nil) }) }) Convey("Given a new regular transaction", t, func() { transaction := models.Transaction{ Type: "transaction", Inputs: []models.Input{ { Outpoint: models.Outpoint{ Index: 0, Txid: "2fb7adb654b373e85c6b5c596cc110dcb6643ee138768f4aa947e9ddb7d91f8d", }, Sig: "1bc4c05ec180932f08b95a8b5be308bb7b90c4d047720c4953440ea7cf56ba38b7e3b52ae586b594a6ae6649d8be0ae3d6944ffe9a7c5894622c33b9df276909", }, }, Outputs: []models.Output{ { Pubkey: "857debb2084fc8c87dec10d305993e781d9c9dbf6a81762b2f245095ae6b8fb9", Value: 50, }, }, } Convey("When GetType is called, 'transaction' should be returned", func() { So(transaction.GetType(), ShouldEqual, "transaction") }) Convey("When GetEntity is called, the transaction should be returned", func() { So(transaction.GetEntity(), ShouldResemble, &transaction) }) Convey("When Validate is called, no error should be returned", func() { So(transaction.Validate(), ShouldEqual, nil) }) Convey("When IsCoinbaseTx is called, 'false' should be returned", func() { So(transaction.IsCoinbaseTx(), ShouldEqual, false) }) Convey("When IsTxInput is called with an input, 'true' should be returned", func() { So(transaction.IsTxInput("2fb7adb654b373e85c6b5c596cc110dcb6643ee138768f4aa947e9ddb7d91f8d"), ShouldEqual, true) }) Convey("When IsTxInput is called with a non-input, 'false' should be returned", func() { So(transaction.IsTxInput("dasdfasfds"), ShouldEqual, false) }) Convey("When Hash is called, the hash of the cannonical json and no error should be returned", func() { transactionJSON, _ := json.MarshalCanonical(transaction) hashSum := sha256.Sum256(transactionJSON) res, err := transaction.Hash() So(hex.EncodeToString(hashSum[:]), ShouldEqual, res) So(err, ShouldEqual, nil) }) Convey("When VerifySign is called with a correct signature, 'true' should be returned", func() { So(transaction.VerifySign("1bc4c05ec180932f08b95a8b5be308bb7b90c4d047720c4953440ea7cf56ba38b7e3b52ae586b594a6ae6649d8be0ae3d6944ffe9a7c5894622c33b9df276909", "57558a6dae91ac3ab8caf3f543eac9c51cba4ac680ba5ba0d81b5575dc06bc46"), ShouldEqual, true) }) Convey("When VerifySign is called with an incorrect signature, 'false' should be returned", func() { So(transaction.VerifySign("", ""), ShouldEqual, false) }) }) Convey("Given a new incorrect regular transaction", t, func() { Convey("With two inputs pointing to the same outpoint", func() { transaction := models.Transaction{ Type: "transaction", Inputs: []models.Input{ { Outpoint: models.Outpoint{ Index: 0, Txid: "2fb7adb654b373e85c6b5c596cc110dcb6643ee138768f4aa947e9ddb7d91f8d", }, Sig: "1bc4c05ec180932f08b95a8b5be308bb7b90c4d047720c4953440ea7cf56ba38b7e3b52ae586b594a6ae6649d8be0ae3d6944ffe9a7c5894622c33b9df276909", }, { Outpoint: models.Outpoint{ Index: 0, Txid: "2fb7adb654b373e85c6b5c596cc110dcb6643ee138768f4aa947e9ddb7d91f8d", }, Sig: "1bc4c05ec180932f08b95a8b5be308bb7b90c4d047720c4953440ea7cf56ba38b7e3b52ae586b594a6ae6649d8be0ae3d6944ffe9a7c5894622c33b9df276909", }, }, Outputs: []models.Output{ { Pubkey: "857debb2084fc8c87dec10d305993e781d9c9dbf6a81762b2f245095ae6b8fb9", Value: 50, }, }, } Convey("When Validate is called, an error should be returned", func() { So(transaction.Validate().Error(), ShouldEqual, "multiple transaction inputs with same outpoint found") }) }) Convey("With negative outpoint index value", func() { transaction := models.Transaction{ Type: "transaction", Inputs: []models.Input{ { Outpoint: models.Outpoint{ Index: -10, Txid: "2fb7adb654b373e85c6b5c596cc110dcb6643ee138768f4aa947e9ddb7d91f8d", }, Sig: "1bc4c05ec180932f08b95a8b5be308bb7b90c4d047720c4953440ea7cf56ba38b7e3b52ae586b594a6ae6649d8be0ae3d6944ffe9a7c5894622c33b9df276909", }, }, Outputs: []models.Output{ { Pubkey: "857debb2084fc8c87dec10d305993e781d9c9dbf6a81762b2f245095ae6b8fb9", Value: 50, }, }, } Convey("When Validate is called, an error should be returned", func() { So(transaction.Validate().Error(), ShouldEqual, "transaction input index cannot be negative") }) }) Convey("With a pubkey that is not hex string", func() { transaction := models.Transaction{ Type: "transaction", Inputs: []models.Input{ { Outpoint: models.Outpoint{ Index: 0, Txid: "2fb7adb654b373e85c6b5c596cc110dcb6643ee138768f4aa947e9ddb7d91f8d", }, Sig: "1bc4c05ec180932f08b95a8b5be308bb7b90c4d047720c4953440ea7cf56ba38b7e3b52ae586b594a6ae6649d8be0ae3d6944ffe9a7c5894622c33b9df276909", }, }, Outputs: []models.Output{ { Pubkey: "VeryInvalidPubkeyValueWOWThisIsSoInvalidOhMyGod", Value: 50, }, }, } Convey("When Validate is called, an error should be returned", func() { So(transaction.Validate(), ShouldNotBeNil) }) }) }) }