Initial commit
This commit is contained in:
292
models/tests/block_test.go
Normal file
292
models/tests/block_test.go
Normal file
@@ -0,0 +1,292 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"kerma/models"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/go/canonical/json"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestBlock(t *testing.T) {
|
||||
Convey("Given a new block", t, func() {
|
||||
previd := "00000000a420b7cefa2b7730243316921ed59ffe836e111ca3801f82a4f5360e"
|
||||
txid := "1bb37b637d07100cd26fc063dfd4c39a7931cc88dae3417871219715a5e374af"
|
||||
block := models.Block{
|
||||
Type: "block",
|
||||
Txids: []string{
|
||||
txid,
|
||||
},
|
||||
Nonce: "c5ee71be4ca85b160d352923a84f86f44b7fc4fe60002214bc1236ceedc5c615",
|
||||
Previd: &previd,
|
||||
Created: 1649827795114,
|
||||
Target: "00000002af000000000000000000000000000000000000000000000000000000",
|
||||
Miner: "svatsan",
|
||||
Note: "First block. Yayy, I have 50 bu now!!",
|
||||
}
|
||||
|
||||
Convey("When GetType is called, 'block' should be returned", func() {
|
||||
So(block.GetType(), ShouldEqual, "block")
|
||||
})
|
||||
|
||||
Convey("When GetEntity is called, the block should be returned", func() {
|
||||
So(block.GetEntity(), ShouldResemble, &block)
|
||||
})
|
||||
|
||||
Convey("When Validate is called, no error should be returned", func() {
|
||||
So(block.Validate(), ShouldEqual, nil)
|
||||
})
|
||||
|
||||
Convey("When ContainsTransaction is called with a correct id, 'true' should be returned", func() {
|
||||
So(block.ContainsTransaction(txid), ShouldEqual, true)
|
||||
})
|
||||
|
||||
Convey("When ContainsTransaction is called with an incorrect id, 'false' should be returned", func() {
|
||||
So(block.ContainsTransaction("123"), ShouldEqual, false)
|
||||
})
|
||||
|
||||
Convey("When Hash is called, the hash of the cannonical json and no error should be returned", func() {
|
||||
blockJSON, _ := json.MarshalCanonical(block)
|
||||
hashSum := sha256.Sum256(blockJSON)
|
||||
|
||||
res, err := block.Hash()
|
||||
So(hex.EncodeToString(hashSum[:]), ShouldEqual, res)
|
||||
So(err, ShouldEqual, nil)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Given the genesis block", t, func() {
|
||||
block := models.GetGenesisBlock()
|
||||
gid := "00000000a420b7cefa2b7730243316921ed59ffe836e111ca3801f82a4f5360e"
|
||||
|
||||
Convey("When GetID is called, the block id should be correct", func() {
|
||||
bid := block.GetID()
|
||||
So(bid, ShouldEqual, gid)
|
||||
})
|
||||
|
||||
Convey("When Validate is called, no error should be returned", func() {
|
||||
So(block.Validate(), ShouldBeNil)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Given a new incorrect block", t, func() {
|
||||
previd := "00000000a420b7cefa2b7730243316921ed59ffe836e111ca3801f82a4f5360e"
|
||||
txid := "1bb37b637d07100cd26fc063dfd4c39a7931cc88dae3417871219715a5e374af"
|
||||
|
||||
Convey("When the block is a genesis", func() {
|
||||
block := models.Block{
|
||||
Type: "block",
|
||||
Txids: []string{
|
||||
txid,
|
||||
},
|
||||
Nonce: "c5ee71be4ca85b160d352923a84f86f44b7fc4fe60002214bc1236ceedc5c615",
|
||||
Previd: nil,
|
||||
Created: 1649827795114,
|
||||
Target: "reallyillegalgenesisblock",
|
||||
Miner: "svatsan",
|
||||
Note: "",
|
||||
}
|
||||
|
||||
Convey("When Validate is called, an error should be returned", func() {
|
||||
So(block.Validate().Error(), ShouldEqual, "illegal genesis block detected")
|
||||
})
|
||||
})
|
||||
|
||||
Convey("With an incorrect genesis", func() {
|
||||
block := models.Block{
|
||||
Type: "block",
|
||||
Txids: []string{
|
||||
txid,
|
||||
},
|
||||
Nonce: "c5ee71be4ca85b160d352923a84f86f44b7fc4fe60002214bc1236ceedc5c615",
|
||||
Previd: &previd,
|
||||
Created: 1649827795114,
|
||||
Target: "reallyillegalgenesisblock",
|
||||
Miner: "svatsan",
|
||||
Note: "",
|
||||
}
|
||||
|
||||
Convey("When Validate is called, an error should be returned", func() {
|
||||
So(block.Validate().Error(), ShouldEqual, "incorrect target supplied")
|
||||
})
|
||||
})
|
||||
|
||||
Convey("With a timestamp before genesis", func() {
|
||||
block := models.Block{
|
||||
Type: "block",
|
||||
Txids: []string{
|
||||
txid,
|
||||
},
|
||||
Nonce: "c5ee71be4ca85b160d352923a84f86f44b7fc4fe60002214bc1236ceedc5c615",
|
||||
Previd: &previd,
|
||||
Created: 0,
|
||||
Target: "00000002af000000000000000000000000000000000000000000000000000000",
|
||||
Miner: "svatsan",
|
||||
Note: "",
|
||||
}
|
||||
|
||||
Convey("When Validate is called, an error should be returned", func() {
|
||||
So(block.Validate().Error(), ShouldEqual, "timestamp before genesis block")
|
||||
})
|
||||
})
|
||||
|
||||
Convey("With a timestamp in the future", func() {
|
||||
block := models.Block{
|
||||
Type: "block",
|
||||
Txids: []string{
|
||||
txid,
|
||||
},
|
||||
Nonce: "c5ee71be4ca85b160d352923a84f86f44b7fc4fe60002214bc1236ceedc5c615",
|
||||
Previd: &previd,
|
||||
Created: time.Now().UnixMilli() + 10000000,
|
||||
Target: "00000002af000000000000000000000000000000000000000000000000000000",
|
||||
Miner: "svatsan",
|
||||
Note: "",
|
||||
}
|
||||
|
||||
Convey("When Validate is called, an error should be returned", func() {
|
||||
So(block.Validate().Error(), ShouldEqual, "block created in the future")
|
||||
})
|
||||
})
|
||||
|
||||
Convey("With a miner that is a non-ASCII printible string", func() {
|
||||
block := models.Block{
|
||||
Type: "block",
|
||||
Txids: []string{
|
||||
txid,
|
||||
},
|
||||
Nonce: "c5ee71be4ca85b160d352923a84f86f44b7fc4fe60002214bc1236ceedc5c615",
|
||||
Previd: &previd,
|
||||
Created: time.Now().UnixMilli(),
|
||||
Target: "00000002af000000000000000000000000000000000000000000000000000000",
|
||||
Miner: "Not@nħCIIStrIng",
|
||||
Note: "",
|
||||
}
|
||||
|
||||
Convey("When Validate is called, an error should be returned", func() {
|
||||
So(block.Validate().Error(), ShouldEqual, "miner is not an ascii printable string")
|
||||
})
|
||||
})
|
||||
|
||||
Convey("With a miner that is more than 128 characters long", func() {
|
||||
block := models.Block{
|
||||
Type: "block",
|
||||
Txids: []string{
|
||||
txid,
|
||||
},
|
||||
Nonce: "c5ee71be4ca85b160d352923a84f86f44b7fc4fe60002214bc1236ceedc5c615",
|
||||
Previd: &previd,
|
||||
Created: time.Now().UnixMilli(),
|
||||
Target: "00000002af000000000000000000000000000000000000000000000000000000",
|
||||
Miner: "LongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongMinerName",
|
||||
Note: "",
|
||||
}
|
||||
|
||||
Convey("When Validate is called, an error should be returned", func() {
|
||||
So(block.Validate().Error(), ShouldEqual, "miner length incorrect")
|
||||
})
|
||||
})
|
||||
|
||||
Convey("With a note that is a non-ASCII printible string", func() {
|
||||
block := models.Block{
|
||||
Type: "block",
|
||||
Txids: []string{
|
||||
txid,
|
||||
},
|
||||
Nonce: "c5ee71be4ca85b160d352923a84f86f44b7fc4fe60002214bc1236ceedc5c615",
|
||||
Previd: &previd,
|
||||
Created: time.Now().UnixMilli(),
|
||||
Target: "00000002af000000000000000000000000000000000000000000000000000000",
|
||||
Miner: "",
|
||||
Note: "Not@nħCIIStrIng",
|
||||
}
|
||||
|
||||
Convey("When Validate is called, an error should be returned", func() {
|
||||
So(block.Validate().Error(), ShouldEqual, "note is not an ascii printable string")
|
||||
})
|
||||
})
|
||||
|
||||
Convey("With a note that is more than 128 characters long", func() {
|
||||
block := models.Block{
|
||||
Type: "block",
|
||||
Txids: []string{
|
||||
txid,
|
||||
},
|
||||
Nonce: "c5ee71be4ca85b160d352923a84f86f44b7fc4fe60002214bc1236ceedc5c615",
|
||||
Previd: &previd,
|
||||
Created: time.Now().UnixMilli(),
|
||||
Target: "00000002af000000000000000000000000000000000000000000000000000000",
|
||||
Miner: "",
|
||||
Note: "LongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongNote",
|
||||
}
|
||||
|
||||
Convey("When Validate is called, an error should be returned", func() {
|
||||
So(block.Validate().Error(), ShouldEqual, "note length incorrect")
|
||||
})
|
||||
})
|
||||
|
||||
Convey("With a non-hex encoded nonce", func() {
|
||||
block := models.Block{
|
||||
Type: "block",
|
||||
Txids: []string{
|
||||
txid,
|
||||
},
|
||||
Nonce: "AReallyIncorrectNonce",
|
||||
Previd: &previd,
|
||||
Created: time.Now().UnixMilli(),
|
||||
Target: "00000002af000000000000000000000000000000000000000000000000000000",
|
||||
Miner: "",
|
||||
Note: "",
|
||||
}
|
||||
|
||||
Convey("When Validate is called, an error should be returned", func() {
|
||||
So(block.Validate().Error(), ShouldNotBeNil)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("With a non-hex encoded previd", func() {
|
||||
incorrectPrevId := "incorrectprevid"
|
||||
|
||||
block := models.Block{
|
||||
Type: "block",
|
||||
Txids: []string{
|
||||
txid,
|
||||
},
|
||||
Nonce: "c5ee71be4ca85b160d352923a84f86f44b7fc4fe60002214bc1236ceedc5c615",
|
||||
Previd: &incorrectPrevId,
|
||||
Created: time.Now().UnixMilli(),
|
||||
Target: "00000002af000000000000000000000000000000000000000000000000000000",
|
||||
Miner: "",
|
||||
Note: "",
|
||||
}
|
||||
|
||||
Convey("When Validate is called, an error should be returned", func() {
|
||||
So(block.Validate().Error(), ShouldNotBeNil)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("With an incorrect proof-of-work equation", func() {
|
||||
previd := "00000000a420b7cefa2b7730243316921ed59ffe836e111ca3801f82a4f5360e"
|
||||
txid := "1bb37b637d07100cd26fc063dfd4c39a7931cc88dae3417871219715a5e37412"
|
||||
block := models.Block{
|
||||
Type: "block",
|
||||
Txids: []string{
|
||||
txid,
|
||||
},
|
||||
Nonce: "c5ee71be4ca85b160d352923a84f86f44b7fc4fe60002214bc1236ceedc5c615",
|
||||
Previd: &previd,
|
||||
Created: 1649827795114,
|
||||
Target: "00000002af000000000000000000000000000000000000000000000000000000",
|
||||
Miner: "svatsan",
|
||||
Note: "First block. Yayy, I have 50 bu now!!",
|
||||
}
|
||||
|
||||
Convey("When Validate is called, an error should be returned", func() {
|
||||
So(block.Validate().Error(), ShouldNotBeNil)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
36
models/tests/chaintip_test.go
Normal file
36
models/tests/chaintip_test.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"kerma/models"
|
||||
"testing"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestChaintip(t *testing.T) {
|
||||
Convey("Given a new chaintip", t, func() {
|
||||
blockID := models.GetGenesisBlock().GetID()
|
||||
var chaintip models.Chaintip
|
||||
|
||||
Convey("When the constructor is called with a value", func() {
|
||||
chaintip.Construct(blockID)
|
||||
|
||||
Convey("The value should be set", func() {
|
||||
So(chaintip.Type, ShouldEqual, "chaintip")
|
||||
So(chaintip.BlockID, ShouldEqual, blockID)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When the MarshalJson is called", func() {
|
||||
chaintip.Construct(blockID)
|
||||
|
||||
Convey("The canonical json and no error should be returned", func() {
|
||||
chaintipJSON, err := chaintip.MarshalJson()
|
||||
canonJSON := `{"blockid":"` + blockID + `","type":"chaintip"}`
|
||||
|
||||
So(string(chaintipJSON), ShouldEqualJSON, canonJSON)
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
24
models/tests/error_test.go
Normal file
24
models/tests/error_test.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"kerma/models"
|
||||
"testing"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestError(t *testing.T) {
|
||||
Convey("Given a new error", t, func() {
|
||||
errString := "testing error"
|
||||
var err models.Error
|
||||
|
||||
Convey("When the constructor is called with a value", func() {
|
||||
err.Construct(errString)
|
||||
|
||||
Convey("The value should be set", func() {
|
||||
So(err.Type, ShouldEqual, "error")
|
||||
So(err.Error, ShouldEqual, errString)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
22
models/tests/generic_test.go
Normal file
22
models/tests/generic_test.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"kerma/models"
|
||||
"testing"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestGeneric(t *testing.T) {
|
||||
Convey("Given a new generic request", t, func() {
|
||||
var generic models.Generic
|
||||
|
||||
Convey("When the BuildPeerRequest method is called", func() {
|
||||
generic.BuildPeerRequest()
|
||||
|
||||
Convey("The type should be 'getpeers'", func() {
|
||||
So(generic.Type, ShouldEqual, "getpeers")
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
36
models/tests/hello_test.go
Normal file
36
models/tests/hello_test.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"kerma/models"
|
||||
"testing"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestHello(t *testing.T) {
|
||||
Convey("Given a new hello request", t, func() {
|
||||
var hello models.Hello
|
||||
|
||||
Convey("When the Construct method is called", func() {
|
||||
hello.Construct()
|
||||
|
||||
Convey("A correct hello request should be created", func() {
|
||||
So(hello.Type, ShouldEqual, "hello")
|
||||
So(hello.Version, ShouldEqual, "0.8.0")
|
||||
So(hello.Agent, ShouldEqual, "BadKerma Go Client 0.8.x")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When an invalid request is created", t, func() {
|
||||
var hello models.Hello
|
||||
|
||||
msgJSON := `{"type": "hello", "version": "0.9.0"}`
|
||||
|
||||
Convey("When UnmarshalJSON is called, it should return an error", func() {
|
||||
err := errors.New("hello request not valid")
|
||||
So(hello.UnmarshalJSON([]byte(msgJSON)), ShouldResemble, err)
|
||||
})
|
||||
})
|
||||
}
|
55
models/tests/mempool_test.go
Normal file
55
models/tests/mempool_test.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"kerma/models"
|
||||
"testing"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestMempool(t *testing.T) {
|
||||
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,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
txid := transaction.GetID()
|
||||
var mempool models.Mempool
|
||||
|
||||
Convey("When the constructor is called with a value", func() {
|
||||
mempool.Construct([]string{txid})
|
||||
|
||||
Convey("The value should be set", func() {
|
||||
So(mempool.Type, ShouldEqual, "mempool")
|
||||
So(mempool.Txids[0], ShouldEqual, txid)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When the MarshalJson is called", func() {
|
||||
mempool.Construct([]string{txid})
|
||||
|
||||
Convey("The canonical json and no error should be returned", func() {
|
||||
mempoolJSON, err := mempool.MarshalJson()
|
||||
canonJSON := `{"txids":["` + txid + `"],"type":"mempool"}`
|
||||
|
||||
So(string(mempoolJSON), ShouldEqualJSON, canonJSON)
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
147
models/tests/object_test.go
Normal file
147
models/tests/object_test.go
Normal file
@@ -0,0 +1,147 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"kerma/models"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/go/canonical/json"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestObject(t *testing.T) {
|
||||
Convey("Given a new object wrapper", t, func() {
|
||||
var wrapper models.ObjectWrapper
|
||||
toHash := "thisstringwillbehashed"
|
||||
hashSum := sha256.Sum256([]byte(toHash))
|
||||
oid := hex.EncodeToString(hashSum[:])
|
||||
|
||||
Convey("When the BuildObjectRequest method is called", func() {
|
||||
wrapper.BuildObjectRequest(oid)
|
||||
|
||||
Convey("The object wrapper should be a valid 'getobject' object", func() {
|
||||
So(wrapper.Type, ShouldEqual, "getobject")
|
||||
So(wrapper.ObjectID, ShouldEqual, oid)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When the BuildGossipObject method is called", func() {
|
||||
wrapper.BuildGossipObject(oid)
|
||||
|
||||
Convey("The object wrapper should be a valid 'ihaveobject' object", func() {
|
||||
So(wrapper.Type, ShouldEqual, "ihaveobject")
|
||||
So(wrapper.ObjectID, ShouldEqual, oid)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Given a new object", t, func() {
|
||||
var object models.Object
|
||||
|
||||
Convey("When the object is a valid transaction", func() {
|
||||
raw := json.RawMessage(`
|
||||
{
|
||||
"height":1,
|
||||
"outputs":[
|
||||
{
|
||||
"pubkey":"62b7c521cd9211579cf70fd4099315643767b96711febaa5c76dc3daf27c281c",
|
||||
"value":50000000000000
|
||||
}
|
||||
],
|
||||
"type":"transaction"
|
||||
}`)
|
||||
|
||||
height := uint64(1)
|
||||
|
||||
object = models.Object{
|
||||
Type: "object",
|
||||
Object: raw,
|
||||
}
|
||||
|
||||
transaction := models.Transaction{
|
||||
Type: "transaction",
|
||||
Height: &height,
|
||||
Outputs: []models.Output{
|
||||
{
|
||||
Pubkey: "62b7c521cd9211579cf70fd4099315643767b96711febaa5c76dc3daf27c281c",
|
||||
Value: 50000000000000,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Convey("Calling GetObjectValue should return the transaction and no error", func() {
|
||||
res, err := object.GetObjectValue()
|
||||
So(res, ShouldResemble, &transaction)
|
||||
So(err, ShouldEqual, nil)
|
||||
|
||||
Convey("Calling String should return the string representation of the transaction", func() {
|
||||
So(res.String(), ShouldEqual, transaction.String())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When the object is a valid block", func() {
|
||||
previd := "00000000a420b7cefa2b7730243316921ed59ffe836e111ca3801f82a4f5360e"
|
||||
raw := json.RawMessage(`
|
||||
{
|
||||
"type": "block",
|
||||
"txids": [
|
||||
"1bb37b637d07100cd26fc063dfd4c39a7931cc88dae3417871219715a5e374af"
|
||||
],
|
||||
"nonce": "c5ee71be4ca85b160d352923a84f86f44b7fc4fe60002214bc1236ceedc5c615",
|
||||
"previd": "` + previd + `",
|
||||
"created": 1649827795114,
|
||||
"T": "00000002af000000000000000000000000000000000000000000000000000000",
|
||||
"miner": "svatsan",
|
||||
"note": "First block. Yayy, I have 50 bu now!!"
|
||||
}`)
|
||||
|
||||
object = models.Object{
|
||||
Type: "object",
|
||||
Object: raw,
|
||||
}
|
||||
|
||||
block := models.Block{
|
||||
Type: "block",
|
||||
Txids: []string{
|
||||
"1bb37b637d07100cd26fc063dfd4c39a7931cc88dae3417871219715a5e374af",
|
||||
},
|
||||
Nonce: "c5ee71be4ca85b160d352923a84f86f44b7fc4fe60002214bc1236ceedc5c615",
|
||||
Previd: &previd,
|
||||
Created: 1649827795114,
|
||||
Target: "00000002af000000000000000000000000000000000000000000000000000000",
|
||||
Miner: "svatsan",
|
||||
Note: "First block. Yayy, I have 50 bu now!!",
|
||||
}
|
||||
|
||||
Convey("Calling GetObjectValue should return the block and no error", func() {
|
||||
res, err := object.GetObjectValue()
|
||||
So(err, ShouldEqual, nil)
|
||||
So(res, ShouldResemble, &block)
|
||||
|
||||
Convey("Calling String should return the string representation of the block", func() {
|
||||
So(res.String(), ShouldEqual, block.String())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When the object is a random object", func() {
|
||||
raw := json.RawMessage(`
|
||||
{
|
||||
"key": "value"
|
||||
}`)
|
||||
|
||||
object = models.Object{
|
||||
Type: "object",
|
||||
Object: raw,
|
||||
}
|
||||
|
||||
Convey("Calling GetObjectValue should return a nil object and an error", func() {
|
||||
res, err := object.GetObjectValue()
|
||||
So(res, ShouldEqual, nil)
|
||||
So(err, ShouldNotEqual, nil)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
421
models/tests/state_test.go
Normal file
421
models/tests/state_test.go
Normal file
@@ -0,0 +1,421 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"kerma/helpers"
|
||||
"kerma/models"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/go/canonical/json"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestState(t *testing.T) {
|
||||
// Prepare
|
||||
testDir := t.TempDir()
|
||||
t.Setenv("KERMA_STORE_BASE_DIR", testDir)
|
||||
t.Setenv("KERMA_INITIAL_PEER_LIST", "example.com:18018")
|
||||
|
||||
var validator helpers.Validator
|
||||
var config helpers.Config
|
||||
|
||||
validator.Construct()
|
||||
config.Construct()
|
||||
|
||||
peerList := []models.Peer{
|
||||
{
|
||||
Name: "example.com:18018",
|
||||
Active: false,
|
||||
},
|
||||
}
|
||||
|
||||
Convey("Given a new state object", t, func() {
|
||||
var state models.State
|
||||
|
||||
Convey("When the Construct method is called", func() {
|
||||
state.Construct()
|
||||
|
||||
Convey("A valid object should be created", func() {
|
||||
So(state.Validator, ShouldResemble, validator)
|
||||
So(state.Config, ShouldResemble, config)
|
||||
So(state.PeerList, ShouldResemble, peerList)
|
||||
So(state.Chain, ShouldResemble, []models.Block{*models.GetGenesisBlock()})
|
||||
So(state.Transactions, ShouldResemble, map[string]*models.Transaction{})
|
||||
})
|
||||
})
|
||||
|
||||
state.Construct()
|
||||
Convey("When the CompleteHandshake method is called", func() {
|
||||
Convey("When the peer is already in the list, it should be marked as active", func() {
|
||||
state.CompleteHandshake("example.com:18018")
|
||||
So(state.PeerList[0].Active, ShouldEqual, true)
|
||||
})
|
||||
|
||||
Convey("When the peer is not in the list, it should be created and marked as active", func() {
|
||||
state.CompleteHandshake("valid.host:18018")
|
||||
So(state.PeerList[1].Name, ShouldEqual, "valid.host:18018")
|
||||
So(state.PeerList[1].Active, ShouldEqual, true)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When the CheckForHandshake method is called", func() {
|
||||
Convey("When there is a finished handshake, 'true' should be returned", func() {
|
||||
state.PeerList[0].Active = true
|
||||
So(state.CheckForHandshake("example.com:18018"), ShouldEqual, true)
|
||||
})
|
||||
|
||||
Convey("When there is no finished handshake, 'false' should be returned", func() {
|
||||
So(state.CheckForHandshake("missing.host:18018"), ShouldEqual, false)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When the FetchPeerListResponse method is called, the list of peers should be returned", func() {
|
||||
res := state.FetchPeerListResponse()
|
||||
|
||||
peerListResponse := models.PeerListResponse{
|
||||
Type: "peers",
|
||||
Peers: []string{
|
||||
"example.com:18018",
|
||||
},
|
||||
}
|
||||
|
||||
So(res, ShouldResemble, peerListResponse)
|
||||
})
|
||||
|
||||
Convey("When the FindPeerByName method is called", func() {
|
||||
Convey("When the peer exists, it should be returned", func() {
|
||||
res := state.FindPeerByName("example.com:18018")
|
||||
So(res, ShouldEqual, &state.PeerList[0])
|
||||
})
|
||||
|
||||
Convey("When the peer does not exist, nil should be returned", func() {
|
||||
res := state.FindPeerByName("example1.com:18018")
|
||||
So(res, ShouldEqual, nil)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When the RemovePeerByName method is called", func() {
|
||||
SkipConvey("When the peer exists, it should be removed", func() {
|
||||
prevLen := len(state.PeerList)
|
||||
state.RemovePeerByName("example.com:18018")
|
||||
So(len(state.PeerList), ShouldEqual, prevLen-1)
|
||||
})
|
||||
|
||||
Convey("When the peer does not exist, nothing should be done", func() {
|
||||
prevLen := len(state.PeerList)
|
||||
state.RemovePeerByName("example1.com:18018")
|
||||
So(len(state.PeerList), ShouldEqual, prevLen)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When the ParsePeerListResponse method is called", func() {
|
||||
Convey("With a non-empty input, all new peers should be added to the peer list", func() {
|
||||
peerListResponse := models.PeerListResponse{
|
||||
Type: "peers",
|
||||
Peers: []string{
|
||||
"sub.example.com:18018",
|
||||
"sub1.example.com:18018",
|
||||
},
|
||||
}
|
||||
|
||||
state.ParsePeerListResponse(&peerListResponse)
|
||||
So(state.FindPeerByName("sub.example.com:18018"), ShouldResemble, &models.Peer{Name: "sub.example.com:18018", Active: false})
|
||||
So(state.FindPeerByName("sub1.example.com:18018"), ShouldResemble, &models.Peer{Name: "sub1.example.com:18018", Active: false})
|
||||
})
|
||||
|
||||
Convey("With an empty input, no new peers should be added to the peer list", func() {
|
||||
peerListResponse := models.PeerListResponse{
|
||||
Type: "peers",
|
||||
Peers: []string{},
|
||||
}
|
||||
|
||||
prevLen := len(state.PeerList)
|
||||
state.ParsePeerListResponse(&peerListResponse)
|
||||
So(len(state.PeerList), ShouldEqual, prevLen)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When the DumpPeerListStore method is called, the peer list should be saved to the peer list store file", func() {
|
||||
state.DumpPeerListStore()
|
||||
|
||||
res, oserr := os.Stat(state.Config.PeerListStore)
|
||||
|
||||
file, _ := os.ReadFile(state.Config.PeerListStore)
|
||||
peerList := []string{}
|
||||
err := json.Unmarshal([]byte(file), &peerList)
|
||||
|
||||
So(oserr, ShouldBeNil)
|
||||
So(res.Size(), ShouldNotEqual, 0)
|
||||
So(res.IsDir(), ShouldBeFalse)
|
||||
So(err, ShouldBeNil)
|
||||
So(len(peerList), ShouldEqual, 3)
|
||||
So(peerList[0], ShouldEqual, "example.com:18018")
|
||||
})
|
||||
|
||||
Convey("Given a correct block", func() {
|
||||
previd := "00000000a420b7cefa2b7730243316921ed59ffe836e111ca3801f82a4f5360e"
|
||||
txid := "2a9458a2e75ed8bd0341b3cb2ab21015bbc13f21ea06229340a7b2b75720c4df"
|
||||
height := uint64(1)
|
||||
transaction := models.Transaction{
|
||||
Type: "transaction",
|
||||
Height: &height,
|
||||
Outputs: []models.Output{
|
||||
{
|
||||
Pubkey: "f66c7d51551d344b74e071d3b988d2bc09c3ffa82857302620d14f2469cfbf60",
|
||||
Value: 50000000000000,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
state.Transactions[txid] = &transaction
|
||||
|
||||
block := models.Block{
|
||||
Type: "block",
|
||||
Txids: []string{
|
||||
txid,
|
||||
},
|
||||
Nonce: "000000000000000000000000000000000000000000000000000000009d8b60ea",
|
||||
Previd: &previd,
|
||||
Created: 1624220079,
|
||||
Target: "00000002af000000000000000000000000000000000000000000000000000000",
|
||||
Miner: "Snekel testminer",
|
||||
Note: "First block after genesis with CBTX",
|
||||
}
|
||||
|
||||
Convey("When AppendToChain is called", func() {
|
||||
err := state.AppendToChain(&block)
|
||||
|
||||
Convey("No error should be returned", func() {
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
|
||||
Convey("When GetChainTip is called, the new block should be returned", func() {
|
||||
So(state.GetChainTip(), ShouldResemble, &block)
|
||||
})
|
||||
|
||||
Convey("When GetBlock is called, the new block should be returned", func() {
|
||||
So(state.GetBlock(block.GetID()), ShouldResemble, &block)
|
||||
})
|
||||
|
||||
Convey("When GetMissingTransactionsInBlock is called, an empty array and no error should be returned", func() {
|
||||
res, err := state.GetMissingTransactionsInBlock(&block)
|
||||
So(res, ShouldBeEmpty)
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
|
||||
Convey("When the DumpBlockStore method is called, the blockchain should be saved to the blockchain store file", func() {
|
||||
state.DumpBlockStore()
|
||||
|
||||
res, oserr := os.Stat(state.Config.BlockStore)
|
||||
|
||||
file, _ := os.ReadFile(state.Config.BlockStore)
|
||||
chain := []models.Block{}
|
||||
err := json.Unmarshal([]byte(file), &chain)
|
||||
|
||||
// Set UTXO of both chain blocks and Height, as they are always recalculated
|
||||
genesis := models.GetGenesisBlock()
|
||||
genesis.UTXOSet = map[string]uint64(nil)
|
||||
block.UTXOSet = map[string]uint64(nil)
|
||||
block.Height = 0
|
||||
|
||||
So(oserr, ShouldBeNil)
|
||||
So(res.Size(), ShouldNotEqual, 0)
|
||||
So(res.IsDir(), ShouldBeFalse)
|
||||
So(err, ShouldBeNil)
|
||||
So(len(chain), ShouldEqual, 2)
|
||||
So(&chain[0], ShouldResemble, genesis)
|
||||
So(&chain[1], ShouldResemble, &block)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Given an incorrect block", func() {
|
||||
previd := "00000000a420b7cefa2b7730243316921ed59ffe836e111ca3801f82a4f5360e"
|
||||
txid := "2a9458a2e75ed8bd0341b3cb2ab21015bbc13f21ea06229340a7b2b75720c4df"
|
||||
height := uint64(1)
|
||||
transaction := models.Transaction{
|
||||
Type: "transaction",
|
||||
Height: &height,
|
||||
Outputs: []models.Output{
|
||||
{
|
||||
Pubkey: "f66c7d51551d344b74e071d3b988d2bc09c3ffa82857302620d14f2469cfbf60",
|
||||
Value: 50000000000000,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
state.Transactions[txid] = &transaction
|
||||
|
||||
block := models.Block{
|
||||
Type: "block",
|
||||
Txids: []string{
|
||||
txid,
|
||||
},
|
||||
Nonce: "000000000000000000000000000000000000000000000000000000009d8b60e2",
|
||||
Previd: &previd,
|
||||
Created: 1624220079,
|
||||
Target: "00000002af000000000000000000000000000000000000000000000000000000",
|
||||
Miner: "Snekel testminer",
|
||||
Note: "First block after genesis with CBTX",
|
||||
}
|
||||
|
||||
Convey("When AppendToChain is called", func() {
|
||||
err := state.AppendToChain(&block)
|
||||
|
||||
Convey("An error should be returned", func() {
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Given a valid coinbase transaction", func() {
|
||||
height := uint64(1)
|
||||
transaction := models.Transaction{
|
||||
Type: "transaction",
|
||||
Height: &height,
|
||||
Outputs: []models.Output{
|
||||
{
|
||||
Pubkey: "62b7c521cd9211579cf70fd4099315643767b96711febaa5c76dc3daf27c281c",
|
||||
Value: 50000000000000,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Convey("When AppendTransaction is called", func() {
|
||||
err := state.AppendTransaction(&transaction)
|
||||
|
||||
Convey("No error should be returned", func() {
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
|
||||
Convey("GetTransaction should return the transaction", func() {
|
||||
So(state.GetTransaction(transaction.GetID()), ShouldResemble, &transaction)
|
||||
})
|
||||
|
||||
Convey("GetMempoolTransactionIDs should return an empty array", func() {
|
||||
So(state.GetMempoolTransactionIDs(), ShouldBeEmpty)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Given an invalid coinbase transaction", func() {
|
||||
height := uint64(1)
|
||||
transaction := models.Transaction{
|
||||
Type: "transaction",
|
||||
Height: &height,
|
||||
Outputs: []models.Output{},
|
||||
}
|
||||
|
||||
Convey("When AppendTransaction is called", func() {
|
||||
err := state.AppendTransaction(&transaction)
|
||||
|
||||
Convey("An error should be returned", func() {
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("GetMempoolTransactionIDs should return an empty array", func() {
|
||||
So(state.GetMempoolTransactionIDs(), ShouldBeEmpty)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Given a valid regular transaction", func() {
|
||||
height := uint64(1)
|
||||
coinbase := models.Transaction{
|
||||
Type: "transaction",
|
||||
Height: &height,
|
||||
Confirmed: true,
|
||||
Outputs: []models.Output{
|
||||
{
|
||||
Pubkey: "f66c7d51551d344b74e071d3b988d2bc09c3ffa82857302620d14f2469cfbf60",
|
||||
Value: 50000000000000,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
state.Transactions[coinbase.GetID()] = &coinbase
|
||||
|
||||
transaction := models.Transaction{
|
||||
Type: "transaction",
|
||||
Inputs: []models.Input{
|
||||
{
|
||||
Outpoint: models.Outpoint{
|
||||
Index: 0,
|
||||
Txid: "2a9458a2e75ed8bd0341b3cb2ab21015bbc13f21ea06229340a7b2b75720c4df",
|
||||
},
|
||||
Sig: "49cc4f9a1fb9d600a7debc99150e7909274c8c74edd7ca183626dfe49eb4aa21c6ff0e4c5f0dc2a328ad6b8ba10bf7169d5f42993a94bf67e13afa943b749c0b",
|
||||
},
|
||||
},
|
||||
Outputs: []models.Output{
|
||||
{
|
||||
Pubkey: "c7c2c13afd02be7986dee0f4630df01abdbc950ea379055f1a423a6090f1b2b3",
|
||||
Value: 50,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Convey("When AppendTransaction is called", func() {
|
||||
err := state.AppendTransaction(&transaction)
|
||||
|
||||
Convey("No error should be returned", func() {
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
|
||||
Convey("GetTransaction should return the transaction", func() {
|
||||
So(state.GetTransaction(transaction.GetID()), ShouldResemble, &transaction)
|
||||
})
|
||||
|
||||
Convey("GetMempoolTransactionIDs should return an array with the transaction", func() {
|
||||
So(state.GetMempoolTransactionIDs(), ShouldNotBeEmpty)
|
||||
So(len(state.GetMempoolTransactionIDs()), ShouldEqual, 1)
|
||||
So(state.GetMempoolTransactionIDs()[0], ShouldEqual, transaction.GetID())
|
||||
})
|
||||
|
||||
Convey("When the DumpTransactionStore method is called, the confirmed transactions should be saved to the store file", func() {
|
||||
state.DumpTransactionStore()
|
||||
|
||||
res, oserr := os.Stat(state.Config.TransactionStore)
|
||||
|
||||
file, _ := os.ReadFile(state.Config.TransactionStore)
|
||||
store := map[string]*models.Transaction{}
|
||||
err := json.Unmarshal([]byte(file), &store)
|
||||
|
||||
// Set confirmed to false, as this is set in the parse function
|
||||
coinbase.Confirmed = false
|
||||
|
||||
So(oserr, ShouldBeNil)
|
||||
So(res.Size(), ShouldNotEqual, 0)
|
||||
So(res.IsDir(), ShouldBeFalse)
|
||||
So(err, ShouldBeNil)
|
||||
So(len(store), ShouldEqual, 1)
|
||||
So(store[coinbase.GetID()], ShouldResemble, &coinbase)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Given an invalid regular transaction", func() {
|
||||
transaction := models.Transaction{
|
||||
Type: "transaction",
|
||||
Inputs: []models.Input{},
|
||||
Outputs: []models.Output{
|
||||
{
|
||||
Pubkey: "c7c2c13afd02be7986dee0f4630df01abdbc950ea379055f1a423a6090f1b2b3",
|
||||
Value: 50,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Convey("When AppendTransaction is called", func() {
|
||||
err := state.AppendTransaction(&transaction)
|
||||
|
||||
Convey("An error should be returned", func() {
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("GetMempoolTransactionIDs should return an empty array", func() {
|
||||
So(state.GetMempoolTransactionIDs(), ShouldBeEmpty)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
202
models/tests/transaction_test.go
Normal file
202
models/tests/transaction_test.go
Normal file
@@ -0,0 +1,202 @@
|
||||
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)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
Reference in New Issue
Block a user