OSDN Git Service

V0.1 votetx input (#67)
authorwz <mars@bytom.io>
Fri, 17 May 2019 02:57:28 +0000 (10:57 +0800)
committerPaladz <yzhu101@uottawa.ca>
Fri, 17 May 2019 02:57:28 +0000 (10:57 +0800)
* add unvote struct

* modify ComputeOutputID

* add uint test

* fix

protocol/bc/types/map.go
protocol/bc/types/txinput.go
protocol/bc/types/txinput_test.go
protocol/bc/types/txoutput.go
protocol/bc/types/txoutput_test.go
protocol/bc/types/unvote.go [new file with mode: 0644]

index b4a4ff0..348c203 100644 (file)
@@ -98,6 +98,28 @@ func mapTx(tx *TxData) (headerID bc.Hash, hdr *bc.TxHeader, entryMap map[bc.Hash
                                Ref:   &coinbaseID,
                                Value: &value,
                        }
+
+               case *UnvoteInput:
+                       // create entry for prevout
+                       prog := &bc.Program{VmVersion: inp.VMVersion, Code: inp.ControlProgram}
+                       src := &bc.ValueSource{
+                               Ref:      &inp.SourceID,
+                               Value:    &inp.AssetAmount,
+                               Position: inp.SourcePosition,
+                       }
+                       prevout := bc.NewVoteOutput(src, prog, 0, inp.Vote) // ordinal doesn't matter for prevouts, only for result outputs
+                       prevoutID := addEntry(prevout)
+                       // create entry for spend
+                       spend := bc.NewSpend(&prevoutID, uint64(i))
+                       spend.WitnessArguments = inp.Arguments
+                       spendID := addEntry(spend)
+                       // setup mux
+                       muxSources[i] = &bc.ValueSource{
+                               Ref:   &spendID,
+                               Value: &inp.AssetAmount,
+                       }
+                       spends = append(spends, spend)
+
                }
        }
 
index c9b4e8e..2da0134 100644 (file)
@@ -14,6 +14,7 @@ const (
        IssuanceInputType uint8 = iota
        SpendInputType
        CoinbaseInputType
+       UnvoteInputType
 )
 
 type (
@@ -38,6 +39,9 @@ func (t *TxInput) AssetAmount() bc.AssetAmount {
        switch inp := t.TypedInput.(type) {
        case *SpendInput:
                return inp.AssetAmount
+
+       case *UnvoteInput:
+               return inp.AssetAmount
        }
        return bc.AssetAmount{}
 }
@@ -48,6 +52,8 @@ func (t *TxInput) AssetID() bc.AssetID {
        case *SpendInput:
                return *inp.AssetId
 
+       case *UnvoteInput:
+               return *inp.AssetId
        }
        return bc.AssetID{}
 }
@@ -57,15 +63,23 @@ func (t *TxInput) Amount() uint64 {
        switch inp := t.TypedInput.(type) {
        case *SpendInput:
                return inp.Amount
+
+       case *UnvoteInput:
+               return inp.Amount
        }
        return 0
 }
 
 // ControlProgram return the control program of the spend input
 func (t *TxInput) ControlProgram() []byte {
-       if si, ok := t.TypedInput.(*SpendInput); ok {
-               return si.ControlProgram
+       switch inp := t.TypedInput.(type) {
+       case *SpendInput:
+               return inp.ControlProgram
+
+       case *UnvoteInput:
+               return inp.ControlProgram
        }
+
        return nil
 }
 
@@ -74,6 +88,9 @@ func (t *TxInput) Arguments() [][]byte {
        switch inp := t.TypedInput.(type) {
        case *SpendInput:
                return inp.Arguments
+
+       case *UnvoteInput:
+               return inp.Arguments
        }
        return nil
 }
@@ -83,14 +100,22 @@ func (t *TxInput) SetArguments(args [][]byte) {
        switch inp := t.TypedInput.(type) {
        case *SpendInput:
                inp.Arguments = args
+
+       case *UnvoteInput:
+               inp.Arguments = args
        }
 }
 
 // SpentOutputID calculate the hash of spended output
 func (t *TxInput) SpentOutputID() (o bc.Hash, err error) {
-       if si, ok := t.TypedInput.(*SpendInput); ok {
-               o, err = ComputeOutputID(&si.SpendCommitment)
+       switch inp := t.TypedInput.(type) {
+       case *SpendInput:
+               o, err = ComputeOutputID(&inp.SpendCommitment, SpendInputType, nil)
+
+       case *UnvoteInput:
+               o, err = ComputeOutputID(&inp.SpendCommitment, UnvoteInputType, inp.Vote)
        }
+
        return o, err
 }
 
@@ -122,6 +147,13 @@ func (t *TxInput) readFrom(r *blockchain.Reader) (err error) {
                                return err
                        }
 
+               case UnvoteInputType:
+                       ui := new(UnvoteInput)
+                       t.TypedInput = ui
+                       if ui.UnvoteCommitmentSuffix, err = ui.SpendCommitment.readFrom(r, 1); err != nil {
+                               return err
+                       }
+
                default:
                        return fmt.Errorf("unsupported input type %d", icType[0])
                }
@@ -141,6 +173,15 @@ func (t *TxInput) readFrom(r *blockchain.Reader) (err error) {
                        if inp.Arguments, err = blockchain.ReadVarstrList(r); err != nil {
                                return err
                        }
+
+               case *UnvoteInput:
+                       if inp.Arguments, err = blockchain.ReadVarstrList(r); err != nil {
+                               return err
+                       }
+                       if inp.Vote, err = blockchain.ReadVarstr31(r); err != nil {
+                               return err
+                       }
+
                }
                return nil
        })
@@ -180,6 +221,12 @@ func (t *TxInput) writeInputCommitment(w io.Writer) (err error) {
                if _, err = blockchain.WriteVarstr31(w, inp.Arbitrary); err != nil {
                        return errors.Wrap(err, "writing coinbase arbitrary")
                }
+
+       case *UnvoteInput:
+               if _, err = w.Write([]byte{UnvoteInputType}); err != nil {
+                       return err
+               }
+               return inp.SpendCommitment.writeExtensibleString(w, inp.UnvoteCommitmentSuffix, t.AssetVersion)
        }
        return nil
 }
@@ -193,6 +240,13 @@ func (t *TxInput) writeInputWitness(w io.Writer) error {
        case *SpendInput:
                _, err := blockchain.WriteVarstrList(w, inp.Arguments)
                return err
+
+       case *UnvoteInput:
+               if _, err := blockchain.WriteVarstrList(w, inp.Arguments); err != nil {
+                       return err
+               }
+               _, err := blockchain.WriteVarstr31(w, inp.Vote)
+               return err
        }
        return nil
 }
index 110b8b3..ec78bbe 100644 (file)
@@ -66,6 +66,63 @@ func TestSerializationSpend(t *testing.T) {
        }
 }
 
+func TestSerializationUnvote(t *testing.T) {
+       arguments := [][]byte{
+               []byte("arguments1"),
+               []byte("arguments2"),
+       }
+
+       unvote := NewUnvoteInput(arguments, testutil.MustDecodeHash("fad5195a0c8e3b590b86a3c0a95e7529565888508aecca96e9aeda633002f409"), testutil.MustDecodeAsset("fe9791d71b67ee62515e08723c061b5ccb952a80d804417c8aeedf7f633c524a"), 254354, 3, []byte("spendProgram"), []byte("af594006a40837d9f028daabb6d589df0b9138daefad5683e5233c2646279217294a8d532e60863bcf196625a35fb8ceeffa3c09610eb92dcfb655a947f13269"))
+
+       wantHex := strings.Join([]string{
+               "01", // asset version
+               "54", // input commitment length
+               "03", // unvote type flag
+               "52", // unvote commitment length
+               "fad5195a0c8e3b590b86a3c0a95e7529565888508aecca96e9aeda633002f409", // source id
+               "fe9791d71b67ee62515e08723c061b5ccb952a80d804417c8aeedf7f633c524a", // assetID
+               "92c30f",                   // amount
+               "03",                       // source position
+               "01",                       // vm version
+               "0c",                       // unvote program length
+               "7370656e6450726f6772616d", // unvote program
+               "9901",                     // witness length
+               "02",                       // argument array length
+               "0a",                       // first argument length
+               "617267756d656e747331",     // first argument data
+               "0a",                       // second argument length
+               "617267756d656e747332",     // second argument data
+               "8001",                     //xpub length
+               "6166353934303036613430383337643966303238646161626236643538396466306239313338646165666164353638336535323333633236343632373932313732393461386435333265363038363362636631393636323561333566623863656566666133633039363130656239326463666236353561393437663133323639", //voter xpub
+       }, "")
+
+       // Test convert struct to hex
+       var buffer bytes.Buffer
+       if err := unvote.writeTo(&buffer); err != nil {
+               t.Fatal(err)
+       }
+
+       gotHex := hex.EncodeToString(buffer.Bytes())
+       if gotHex != wantHex {
+               t.Errorf("serialization bytes = %s want %s", gotHex, wantHex)
+       }
+
+       // Test convert hex to struct
+       var gotUnvote TxInput
+       decodeHex, err := hex.DecodeString(wantHex)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       if err := gotUnvote.readFrom(blockchain.NewReader(decodeHex)); err != nil {
+               t.Fatal(err)
+       }
+
+       if !testutil.DeepEqual(*unvote, gotUnvote) {
+               t.Errorf("expected marshaled/unmarshaled txinput to be:\n%sgot:\n%s", spew.Sdump(*unvote), spew.Sdump(gotUnvote))
+       }
+}
+
 func TestSerializationCoinbase(t *testing.T) {
        coinbase := NewCoinbaseInput([]byte("arbitrary"))
        wantHex := strings.Join([]string{
index 7cc2b61..aca9920 100644 (file)
@@ -206,7 +206,7 @@ func (to *TxOutput) writeOutputCommitment(w io.Writer) error {
 
 // ComputeOutputID assembles an intra-chain(!) output entry given a spend
 // commitment and computes and returns its corresponding entry ID.
-func ComputeOutputID(sc *SpendCommitment) (h bc.Hash, err error) {
+func ComputeOutputID(sc *SpendCommitment, inputType uint8, vote []byte) (h bc.Hash, err error) {
        defer func() {
                if r, ok := recover().(error); ok {
                        err = r
@@ -217,7 +217,15 @@ func ComputeOutputID(sc *SpendCommitment) (h bc.Hash, err error) {
                Value:    &sc.AssetAmount,
                Position: sc.SourcePosition,
        }
-       o := bc.NewIntraChainOutput(src, &bc.Program{VmVersion: sc.VMVersion, Code: sc.ControlProgram}, 0)
+       var o bc.Entry
+       switch inputType {
+       case SpendInputType:
+               o = bc.NewIntraChainOutput(src, &bc.Program{VmVersion: sc.VMVersion, Code: sc.ControlProgram}, 0)
+       case UnvoteInputType:
+               o = bc.NewVoteOutput(src, &bc.Program{VmVersion: sc.VMVersion, Code: sc.ControlProgram}, 0, vote)
+       default:
+               return h, fmt.Errorf("Input type error:[%v]", inputType)
+       }
 
        h = bc.EntryID(o)
        return h, nil
index b978073..a6c4eba 100644 (file)
@@ -106,6 +106,8 @@ func TestComputeOutputID(t *testing.T) {
        btmAssetID := testutil.MustDecodeAsset("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
        cases := []struct {
                sc           *SpendCommitment
+               inputType    uint8
+               vote         []byte
                wantOutputID string
        }{
                {
@@ -116,6 +118,8 @@ func TestComputeOutputID(t *testing.T) {
                                VMVersion:      1,
                                ControlProgram: testutil.MustDecodeHexString("0014cb9f2391bafe2bc1159b2c4c8a0f17ba1b4dd94e"),
                        },
+                       inputType:    SpendInputType,
+                       vote:         nil,
                        wantOutputID: "73eea4d38b22ffd60fc30d0941f3875f45e29d424227bfde100193a08568605b",
                },
                {
@@ -126,12 +130,38 @@ func TestComputeOutputID(t *testing.T) {
                                VMVersion:      1,
                                ControlProgram: testutil.MustDecodeHexString("001418549d84daf53344d32563830c7cf979dc19d5c0"),
                        },
+                       inputType:    SpendInputType,
+                       vote:         nil,
                        wantOutputID: "8371e76fd1c873503a326268bfd286ffe13009a0f1140d2c858e8187825696ab",
                },
+               {
+                       sc: &SpendCommitment{
+                               AssetAmount:    bc.AssetAmount{AssetId: &btmAssetID, Amount: 999},
+                               SourceID:       testutil.MustDecodeHash("993d3797fa3b2d958f300e599987dc10904b13f56ce89d158f60f9131424e0e2"),
+                               SourcePosition: 2,
+                               VMVersion:      1,
+                               ControlProgram: testutil.MustDecodeHexString("00145c47f3a0dd3e1e9956fe5b0f897072ed33f9efb9"),
+                       },
+                       inputType:    UnvoteInputType,
+                       vote:         []byte("af594006a40837d9f028daabb6d589df0b9138daefad5683e5233c2646279217294a8d532e60863bcf196625a35fb8ceeffa3c09610eb92dcfb655a947f13269"),
+                       wantOutputID: "c95701822db14f5c647158762e4d4b9bff270bfd3040f0ca32cb87c18e377429",
+               },
+               {
+                       sc: &SpendCommitment{
+                               AssetAmount:    bc.AssetAmount{AssetId: &btmAssetID, Amount: 999},
+                               SourceID:       testutil.MustDecodeHash("993d3797fa3b2d958f300e599987dc10904b13f56ce89d158f60f9131424e0e2"),
+                               SourcePosition: 2,
+                               VMVersion:      1,
+                               ControlProgram: testutil.MustDecodeHexString("00145c47f3a0dd3e1e9956fe5b0f897072ed33f9efb9"),
+                       },
+                       inputType:    UnvoteInputType,
+                       vote:         []byte(""),
+                       wantOutputID: "8f17b871f3fd07bfe778e92c272e26d4bb19258c90af08310ef32feb526eaf9c",
+               },
        }
 
        for _, c := range cases {
-               outputID, err := ComputeOutputID(c.sc)
+               outputID, err := ComputeOutputID(c.sc, c.inputType, c.vote)
                if err != nil {
                        t.Fatal(err)
                }
diff --git a/protocol/bc/types/unvote.go b/protocol/bc/types/unvote.go
new file mode 100644 (file)
index 0000000..810a8f3
--- /dev/null
@@ -0,0 +1,38 @@
+package types
+
+import (
+       "github.com/vapor/protocol/bc"
+)
+
+// UnvoteInput satisfies the TypedInput interface and represents a unvote transaction.
+type UnvoteInput struct {
+       UnvoteCommitmentSuffix []byte   // The unconsumed suffix of the output commitment
+       Arguments              [][]byte // Witness
+       Vote                   []byte   // voter xpub
+       SpendCommitment
+}
+
+// NewUnvoteInput create a new UnvoteInput struct.
+func NewUnvoteInput(arguments [][]byte, sourceID bc.Hash, assetID bc.AssetID, amount, sourcePos uint64, controlProgram []byte, vote []byte) *TxInput {
+       sc := SpendCommitment{
+               AssetAmount: bc.AssetAmount{
+                       AssetId: &assetID,
+                       Amount:  amount,
+               },
+               SourceID:       sourceID,
+               SourcePosition: sourcePos,
+               VMVersion:      1,
+               ControlProgram: controlProgram,
+       }
+       return &TxInput{
+               AssetVersion: 1,
+               TypedInput: &UnvoteInput{
+                       SpendCommitment: sc,
+                       Arguments:       arguments,
+                       Vote:            vote,
+               },
+       }
+}
+
+// InputType is the interface function for return the input type.
+func (ui *UnvoteInput) InputType() uint8 { return SpendInputType }