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)
+
}
}
IssuanceInputType uint8 = iota
SpendInputType
CoinbaseInputType
+ UnvoteInputType
)
type (
switch inp := t.TypedInput.(type) {
case *SpendInput:
return inp.AssetAmount
+
+ case *UnvoteInput:
+ return inp.AssetAmount
}
return bc.AssetAmount{}
}
case *SpendInput:
return *inp.AssetId
+ case *UnvoteInput:
+ return *inp.AssetId
}
return bc.AssetID{}
}
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
}
switch inp := t.TypedInput.(type) {
case *SpendInput:
return inp.Arguments
+
+ case *UnvoteInput:
+ return inp.Arguments
}
return nil
}
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
}
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])
}
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
})
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
}
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
}
}
}
+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{
// 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
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
btmAssetID := testutil.MustDecodeAsset("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
cases := []struct {
sc *SpendCommitment
+ inputType uint8
+ vote []byte
wantOutputID string
}{
{
VMVersion: 1,
ControlProgram: testutil.MustDecodeHexString("0014cb9f2391bafe2bc1159b2c4c8a0f17ba1b4dd94e"),
},
+ inputType: SpendInputType,
+ vote: nil,
wantOutputID: "73eea4d38b22ffd60fc30d0941f3875f45e29d424227bfde100193a08568605b",
},
{
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)
}
--- /dev/null
+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 }