Merge pull request #920 from nspcc-dev/neo3/transaction/cosigners

core: implement cosigners mechanism
This commit is contained in:
Roman Khimov 2020-05-04 17:15:07 +03:00 committed by GitHub
commit ec6ade4c9b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 420 additions and 158 deletions

View file

@ -79,16 +79,12 @@ func getTX(t *testing.B, wif *keys.WIF) *transaction.Transaction {
tx := transaction.NewInvocationTX([]byte{0x51}, 1) tx := transaction.NewInvocationTX([]byte{0x51}, 1)
tx.Version = 0 tx.Version = 0
tx.Sender = fromAddressHash
tx.Attributes = append(tx.Attributes, tx.Attributes = append(tx.Attributes,
transaction.Attribute{ transaction.Attribute{
Usage: transaction.Description, Usage: transaction.Description,
Data: []byte(randString(10)), Data: []byte(randString(10)),
}) })
tx.Attributes = append(tx.Attributes,
transaction.Attribute{
Usage: transaction.Script,
Data: fromAddressHash.BytesBE(),
})
return tx return tx
} }

View file

@ -1548,17 +1548,6 @@ func (bc *Blockchain) GetScriptHashesForVerifying(t *transaction.Transaction) ([
for i := range references { for i := range references {
hashes[references[i].Out.ScriptHash] = true hashes[references[i].Out.ScriptHash] = true
} }
for _, a := range t.Attributes {
if a.Usage == transaction.Script {
h, err := util.Uint160DecodeBytesBE(a.Data)
if err != nil {
return nil, err
}
if _, ok := hashes[h]; !ok {
hashes[h] = true
}
}
}
for a, outputs := range t.GroupOutputByAssetID() { for a, outputs := range t.GroupOutputByAssetID() {
as := bc.GetAssetState(a) as := bc.GetAssetState(a)
@ -1575,6 +1564,9 @@ func (bc *Blockchain) GetScriptHashesForVerifying(t *transaction.Transaction) ([
} }
} }
hashes[t.Sender] = true hashes[t.Sender] = true
for _, c := range t.Cosigners {
hashes[c.Account] = true
}
switch t.Type { switch t.Type {
case transaction.ClaimType: case transaction.ClaimType:
claim := t.Data.(*transaction.ClaimTX) claim := t.Data.(*transaction.ClaimTX)

View file

@ -2,6 +2,7 @@ package runtime
import ( import (
"github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/interop"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm"
@ -10,7 +11,12 @@ import (
// CheckHashedWitness checks given hash against current list of script hashes // CheckHashedWitness checks given hash against current list of script hashes
// for verifying in the interop context. // for verifying in the interop context.
func CheckHashedWitness(ic *interop.Context, hash util.Uint160) (bool, error) { func CheckHashedWitness(ic *interop.Context, v vm.ScriptHashGetter, hash util.Uint160) (bool, error) {
if tx, ok := ic.Container.(*transaction.Transaction); ok {
return checkScope(tx, v, hash)
}
// only for non-Transaction types (Block, etc.)
hashes, err := ic.Chain.GetScriptHashesForVerifying(ic.Tx) hashes, err := ic.Chain.GetScriptHashesForVerifying(ic.Tx)
if err != nil { if err != nil {
return false, errors.Wrap(err, "failed to get script hashes") return false, errors.Wrap(err, "failed to get script hashes")
@ -23,10 +29,57 @@ func CheckHashedWitness(ic *interop.Context, hash util.Uint160) (bool, error) {
return false, nil return false, nil
} }
func checkScope(tx *transaction.Transaction, v vm.ScriptHashGetter, hash util.Uint160) (bool, error) {
for _, c := range tx.Cosigners {
if c.Account == hash {
if c.Scopes == transaction.Global {
return true, nil
}
if c.Scopes&transaction.CalledByEntry != 0 {
callingScriptHash := v.GetCallingScriptHash()
entryScriptHash := v.GetEntryScriptHash()
if callingScriptHash == entryScriptHash {
return true, nil
}
}
if c.Scopes&transaction.CustomContracts != 0 {
currentScriptHash := v.GetCurrentScriptHash()
for _, allowedContract := range c.AllowedContracts {
if allowedContract == currentScriptHash {
return true, nil
}
}
}
if c.Scopes&transaction.CustomGroups != 0 {
return true, nil
// TODO: we don't currently have Manifest field in ContractState
/*callingScriptHash := v.GetCallingScriptHash()
if callingScriptHash.Equals(util.Uint160{}){
return false, nil
}
cs, err := ic.DAO.GetContractState(callingScriptHash)
if err != nil {
return false, err
}
// check if the current group is the required one
for _, allowedGroup := range c.AllowedGroups {
for _, group := range cs.Manifest.Groups {
if group.PublicKey.Equal(allowedGroup) {
return true, nil
}
}
}*/
}
return false, nil
}
}
return false, nil
}
// CheckKeyedWitness checks hash of signature check contract with a given public // CheckKeyedWitness checks hash of signature check contract with a given public
// key against current list of script hashes for verifying in the interop context. // key against current list of script hashes for verifying in the interop context.
func CheckKeyedWitness(ic *interop.Context, key *keys.PublicKey) (bool, error) { func CheckKeyedWitness(ic *interop.Context, v vm.ScriptHashGetter, key *keys.PublicKey) (bool, error) {
return CheckHashedWitness(ic, key.GetScriptHash()) return CheckHashedWitness(ic, v, key.GetScriptHash())
} }
// CheckWitness checks witnesses. // CheckWitness checks witnesses.
@ -42,9 +95,9 @@ func CheckWitness(ic *interop.Context, v *vm.VM) error {
if err != nil { if err != nil {
return errors.New("parameter given is neither a key nor a hash") return errors.New("parameter given is neither a key nor a hash")
} }
res, err = CheckKeyedWitness(ic, key) res, err = CheckKeyedWitness(ic, v, key)
} else { } else {
res, err = CheckHashedWitness(ic, hash) res, err = CheckHashedWitness(ic, v, hash)
} }
if err != nil { if err != nil {
return errors.Wrap(err, "failed to check") return errors.Wrap(err, "failed to check")

View file

@ -540,7 +540,7 @@ func contractMigrate(ic *interop.Context, v *vm.VM) error {
return err return err
} }
if contract.HasStorage() { if contract.HasStorage() {
hash := v.GetContextScriptHash(0) hash := v.GetCurrentScriptHash()
siMap, err := ic.DAO.GetStorageItems(hash) siMap, err := ic.DAO.GetStorageItems(hash)
if err != nil { if err != nil {
return err return err
@ -601,7 +601,7 @@ func assetCreate(ic *interop.Context, v *vm.VM) error {
if owner.IsInfinity() { if owner.IsInfinity() {
return errors.New("can't have infinity as an owner key") return errors.New("can't have infinity as an owner key")
} }
witnessOk, err := runtime.CheckKeyedWitness(ic, owner) witnessOk, err := runtime.CheckKeyedWitness(ic, v, owner)
if err != nil { if err != nil {
return err return err
} }

View file

@ -292,7 +292,7 @@ func runtimeNotify(ic *interop.Context, v *vm.VM) error {
if err != nil { if err != nil {
item = vm.NewByteArrayItem([]byte(fmt.Sprintf("bad notification: %v", err))) item = vm.NewByteArrayItem([]byte(fmt.Sprintf("bad notification: %v", err)))
} }
ne := state.NotificationEvent{ScriptHash: v.GetContextScriptHash(0), Item: item} ne := state.NotificationEvent{ScriptHash: v.GetCurrentScriptHash(), Item: item}
ic.Notifications = append(ic.Notifications, ne) ic.Notifications = append(ic.Notifications, ne)
return nil return nil
} }
@ -301,7 +301,7 @@ func runtimeNotify(ic *interop.Context, v *vm.VM) error {
func runtimeLog(ic *interop.Context, v *vm.VM) error { func runtimeLog(ic *interop.Context, v *vm.VM) error {
msg := fmt.Sprintf("%q", v.Estack().Pop().Bytes()) msg := fmt.Sprintf("%q", v.Estack().Pop().Bytes())
ic.Log.Info("runtime log", ic.Log.Info("runtime log",
zap.Stringer("script", v.GetContextScriptHash(0)), zap.Stringer("script", v.GetCurrentScriptHash()),
zap.String("logs", msg)) zap.String("logs", msg))
return nil return nil
} }
@ -383,7 +383,7 @@ func storageGet(ic *interop.Context, v *vm.VM) error {
// storageGetContext returns storage context (scripthash). // storageGetContext returns storage context (scripthash).
func storageGetContext(ic *interop.Context, v *vm.VM) error { func storageGetContext(ic *interop.Context, v *vm.VM) error {
sc := &StorageContext{ sc := &StorageContext{
ScriptHash: v.GetContextScriptHash(0), ScriptHash: v.GetCurrentScriptHash(),
ReadOnly: false, ReadOnly: false,
} }
v.Estack().PushVal(vm.NewInteropItem(sc)) v.Estack().PushVal(vm.NewInteropItem(sc))
@ -393,7 +393,7 @@ func storageGetContext(ic *interop.Context, v *vm.VM) error {
// storageGetReadOnlyContext returns read-only context (scripthash). // storageGetReadOnlyContext returns read-only context (scripthash).
func storageGetReadOnlyContext(ic *interop.Context, v *vm.VM) error { func storageGetReadOnlyContext(ic *interop.Context, v *vm.VM) error {
sc := &StorageContext{ sc := &StorageContext{
ScriptHash: v.GetContextScriptHash(0), ScriptHash: v.GetCurrentScriptHash(),
ReadOnly: true, ReadOnly: true,
} }
v.Estack().PushVal(vm.NewInteropItem(sc)) v.Estack().PushVal(vm.NewInteropItem(sc))
@ -475,7 +475,7 @@ func contractDestroy(ic *interop.Context, v *vm.VM) error {
if ic.Trigger != trigger.Application { if ic.Trigger != trigger.Application {
return errors.New("can't destroy contract when not triggered by application") return errors.New("can't destroy contract when not triggered by application")
} }
hash := v.GetContextScriptHash(0) hash := v.GetCurrentScriptHash()
cs, err := ic.DAO.GetContractState(hash) cs, err := ic.DAO.GetContractState(hash)
if err != nil { if err != nil {
return nil return nil

View file

@ -69,7 +69,7 @@ func (cs *Contracts) GetNativeInterop(ic *interop.Context) func(uint32) *vm.Inte
// getNativeInterop returns native contract interop. // getNativeInterop returns native contract interop.
func getNativeInterop(ic *interop.Context, c interop.Contract) func(v *vm.VM) error { func getNativeInterop(ic *interop.Context, c interop.Contract) func(v *vm.VM) error {
return func(v *vm.VM) error { return func(v *vm.VM) error {
h := v.GetContextScriptHash(0) h := v.GetCurrentScriptHash()
if !h.Equals(c.Metadata().Hash) { if !h.Equals(c.Metadata().Hash) {
return errors.New("invalid hash") return errors.New("invalid hash")
} }

View file

@ -251,7 +251,7 @@ func (n *NEO) vote(ic *interop.Context, args []vm.StackItem) vm.StackItem {
// VoteInternal votes from account h for validarors specified in pubs. // VoteInternal votes from account h for validarors specified in pubs.
func (n *NEO) VoteInternal(ic *interop.Context, h util.Uint160, pubs keys.PublicKeys) error { func (n *NEO) VoteInternal(ic *interop.Context, h util.Uint160, pubs keys.PublicKeys) error {
ok, err := runtime.CheckHashedWitness(ic, h) ok, err := runtime.CheckHashedWitness(ic, neoScriptHash{hash: n.Hash}, h)
if err != nil { if err != nil {
return err return err
} else if !ok { } else if !ok {
@ -479,3 +479,24 @@ func toPublicKey(s vm.StackItem) *keys.PublicKey {
} }
return pub return pub
} }
// scriptHash is an auxiliary structure which implements ScriptHashGetter
// interface over NEO native contract and is used for runtime.CheckHashedWitness
type neoScriptHash struct {
hash util.Uint160
}
// GetCallingScriptHash implements ScriptHashGetter interface
func (s neoScriptHash) GetCallingScriptHash() util.Uint160 {
return util.Uint160{}
}
// GetEntryScriptHash implements ScriptHashGetter interface
func (s neoScriptHash) GetEntryScriptHash() util.Uint160 {
return s.hash
}
// GetCurrentScriptHash implements ScriptHashGetter interface
func (s neoScriptHash) GetCurrentScriptHash() util.Uint160 {
return s.hash
}

View file

@ -49,7 +49,7 @@ func NewAccount(scriptHash util.Uint160) *Account {
// DecodeBinary decodes Account from the given BinReader. // DecodeBinary decodes Account from the given BinReader.
func (s *Account) DecodeBinary(br *io.BinReader) { func (s *Account) DecodeBinary(br *io.BinReader) {
s.Version = uint8(br.ReadB()) s.Version = uint8(br.ReadB())
br.ReadBytes(s.ScriptHash[:]) s.ScriptHash.DecodeBinary(br)
s.IsFrozen = br.ReadBool() s.IsFrozen = br.ReadBool()
s.Balances = make(map[util.Uint256][]UnspentBalance) s.Balances = make(map[util.Uint256][]UnspentBalance)
@ -73,7 +73,7 @@ func (s *Account) DecodeBinary(br *io.BinReader) {
// EncodeBinary encodes Account to the given BinWriter. // EncodeBinary encodes Account to the given BinWriter.
func (s *Account) EncodeBinary(bw *io.BinWriter) { func (s *Account) EncodeBinary(bw *io.BinWriter) {
bw.WriteB(byte(s.Version)) bw.WriteB(byte(s.Version))
bw.WriteBytes(s.ScriptHash[:]) s.ScriptHash.EncodeBinary(bw)
bw.WriteBool(s.IsFrozen) bw.WriteBool(s.IsFrozen)
bw.WriteVarUint(uint64(len(s.Balances))) bw.WriteVarUint(uint64(len(s.Balances)))

View file

@ -37,11 +37,11 @@ func (a *Asset) DecodeBinary(br *io.BinReader) {
a.Available.DecodeBinary(br) a.Available.DecodeBinary(br)
a.Precision = uint8(br.ReadB()) a.Precision = uint8(br.ReadB())
a.FeeMode = uint8(br.ReadB()) a.FeeMode = uint8(br.ReadB())
br.ReadBytes(a.FeeAddress[:]) a.FeeAddress.DecodeBinary(br)
a.Owner.DecodeBinary(br) a.Owner.DecodeBinary(br)
br.ReadBytes(a.Admin[:]) a.Admin.DecodeBinary(br)
br.ReadBytes(a.Issuer[:]) a.Issuer.DecodeBinary(br)
a.Expiration = br.ReadU32LE() a.Expiration = br.ReadU32LE()
a.IsFrozen = br.ReadBool() a.IsFrozen = br.ReadBool()
} }
@ -55,12 +55,12 @@ func (a *Asset) EncodeBinary(bw *io.BinWriter) {
a.Available.EncodeBinary(bw) a.Available.EncodeBinary(bw)
bw.WriteB(byte(a.Precision)) bw.WriteB(byte(a.Precision))
bw.WriteB(byte(a.FeeMode)) bw.WriteB(byte(a.FeeMode))
bw.WriteBytes(a.FeeAddress[:]) a.FeeAddress.EncodeBinary(bw)
a.Owner.EncodeBinary(bw) a.Owner.EncodeBinary(bw)
bw.WriteBytes(a.Admin[:]) a.Admin.EncodeBinary(bw)
bw.WriteBytes(a.Issuer[:]) a.Issuer.EncodeBinary(bw)
bw.WriteU32LE(a.Expiration) bw.WriteU32LE(a.Expiration)
bw.WriteBool(a.IsFrozen) bw.WriteBool(a.IsFrozen)
} }

View file

@ -28,13 +28,13 @@ type AppExecResult struct {
// EncodeBinary implements the Serializable interface. // EncodeBinary implements the Serializable interface.
func (ne *NotificationEvent) EncodeBinary(w *io.BinWriter) { func (ne *NotificationEvent) EncodeBinary(w *io.BinWriter) {
w.WriteBytes(ne.ScriptHash[:]) ne.ScriptHash.EncodeBinary(w)
vm.EncodeBinaryStackItem(ne.Item, w) vm.EncodeBinaryStackItem(ne.Item, w)
} }
// DecodeBinary implements the Serializable interface. // DecodeBinary implements the Serializable interface.
func (ne *NotificationEvent) DecodeBinary(r *io.BinReader) { func (ne *NotificationEvent) DecodeBinary(r *io.BinReader) {
r.ReadBytes(ne.ScriptHash[:]) ne.ScriptHash.DecodeBinary(r)
ne.Item = vm.DecodeBinaryStackItem(r) ne.Item = vm.DecodeBinaryStackItem(r)
} }

View file

@ -10,7 +10,6 @@ const (
ContractHash AttrUsage = 0x00 ContractHash AttrUsage = 0x00
ECDH02 AttrUsage = 0x02 ECDH02 AttrUsage = 0x02
ECDH03 AttrUsage = 0x03 ECDH03 AttrUsage = 0x03
Script AttrUsage = 0x20
Vote AttrUsage = 0x30 Vote AttrUsage = 0x30
CertURL AttrUsage = 0x80 CertURL AttrUsage = 0x80
DescriptionURL AttrUsage = 0x81 DescriptionURL AttrUsage = 0x81

View file

@ -31,8 +31,6 @@ func (attr *Attribute) DecodeBinary(br *io.BinReader) {
Hash6, Hash7, Hash8, Hash9, Hash10, Hash11, Hash12, Hash13, Hash6, Hash7, Hash8, Hash9, Hash10, Hash11, Hash12, Hash13,
Hash14, Hash15: Hash14, Hash15:
datasize = 32 datasize = 32
case Script:
datasize = 20
case DescriptionURL: case DescriptionURL:
// It's not VarUint as per C# implementation, dunno why // It's not VarUint as per C# implementation, dunno why
var urllen = br.ReadB() var urllen = br.ReadB()
@ -62,7 +60,7 @@ func (attr *Attribute) EncodeBinary(bw *io.BinWriter) {
case DescriptionURL: case DescriptionURL:
bw.WriteB(byte(len(attr.Data))) bw.WriteB(byte(len(attr.Data)))
fallthrough fallthrough
case Script, ContractHash, Vote, Hash1, Hash2, Hash3, Hash4, Hash5, Hash6, case ContractHash, Vote, Hash1, Hash2, Hash3, Hash4, Hash5, Hash6,
Hash7, Hash8, Hash9, Hash10, Hash11, Hash12, Hash13, Hash14, Hash15: Hash7, Hash8, Hash9, Hash10, Hash11, Hash12, Hash13, Hash14, Hash15:
bw.WriteBytes(attr.Data) bw.WriteBytes(attr.Data)
default: default:

View file

@ -11,7 +11,6 @@ func _() {
_ = x[ContractHash-0] _ = x[ContractHash-0]
_ = x[ECDH02-2] _ = x[ECDH02-2]
_ = x[ECDH03-3] _ = x[ECDH03-3]
_ = x[Script-32]
_ = x[Vote-48] _ = x[Vote-48]
_ = x[CertURL-128] _ = x[CertURL-128]
_ = x[DescriptionURL-129] _ = x[DescriptionURL-129]
@ -52,19 +51,18 @@ func _() {
const ( const (
_AttrUsage_name_0 = "ContractHash" _AttrUsage_name_0 = "ContractHash"
_AttrUsage_name_1 = "ECDH02ECDH03" _AttrUsage_name_1 = "ECDH02ECDH03"
_AttrUsage_name_2 = "Script" _AttrUsage_name_2 = "Vote"
_AttrUsage_name_3 = "Vote" _AttrUsage_name_3 = "CertURLDescriptionURL"
_AttrUsage_name_4 = "CertURLDescriptionURL" _AttrUsage_name_4 = "Description"
_AttrUsage_name_5 = "Description" _AttrUsage_name_5 = "Hash1Hash2Hash3Hash4Hash5Hash6Hash7Hash8Hash9Hash10Hash11Hash12Hash13Hash14Hash15"
_AttrUsage_name_6 = "Hash1Hash2Hash3Hash4Hash5Hash6Hash7Hash8Hash9Hash10Hash11Hash12Hash13Hash14Hash15" _AttrUsage_name_6 = "RemarkRemark1Remark2Remark3Remark4Remark5Remark6Remark7Remark8Remark9Remark10Remark11Remark12Remark13Remark14Remark15"
_AttrUsage_name_7 = "RemarkRemark1Remark2Remark3Remark4Remark5Remark6Remark7Remark8Remark9Remark10Remark11Remark12Remark13Remark14Remark15"
) )
var ( var (
_AttrUsage_index_1 = [...]uint8{0, 6, 12} _AttrUsage_index_1 = [...]uint8{0, 6, 12}
_AttrUsage_index_4 = [...]uint8{0, 7, 21} _AttrUsage_index_3 = [...]uint8{0, 7, 21}
_AttrUsage_index_6 = [...]uint8{0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 51, 57, 63, 69, 75, 81} _AttrUsage_index_5 = [...]uint8{0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 51, 57, 63, 69, 75, 81}
_AttrUsage_index_7 = [...]uint8{0, 6, 13, 20, 27, 34, 41, 48, 55, 62, 69, 77, 85, 93, 101, 109, 117} _AttrUsage_index_6 = [...]uint8{0, 6, 13, 20, 27, 34, 41, 48, 55, 62, 69, 77, 85, 93, 101, 109, 117}
) )
func (i AttrUsage) String() string { func (i AttrUsage) String() string {
@ -74,21 +72,19 @@ func (i AttrUsage) String() string {
case 2 <= i && i <= 3: case 2 <= i && i <= 3:
i -= 2 i -= 2
return _AttrUsage_name_1[_AttrUsage_index_1[i]:_AttrUsage_index_1[i+1]] return _AttrUsage_name_1[_AttrUsage_index_1[i]:_AttrUsage_index_1[i+1]]
case i == 32:
return _AttrUsage_name_2
case i == 48: case i == 48:
return _AttrUsage_name_3 return _AttrUsage_name_2
case 128 <= i && i <= 129: case 128 <= i && i <= 129:
i -= 128 i -= 128
return _AttrUsage_name_4[_AttrUsage_index_4[i]:_AttrUsage_index_4[i+1]] return _AttrUsage_name_3[_AttrUsage_index_3[i]:_AttrUsage_index_3[i+1]]
case i == 144: case i == 144:
return _AttrUsage_name_5 return _AttrUsage_name_4
case 161 <= i && i <= 175: case 161 <= i && i <= 175:
i -= 161 i -= 161
return _AttrUsage_name_6[_AttrUsage_index_6[i]:_AttrUsage_index_6[i+1]] return _AttrUsage_name_5[_AttrUsage_index_5[i]:_AttrUsage_index_5[i+1]]
case 240 <= i && i <= 255: case 240 <= i && i <= 255:
i -= 240 i -= 240
return _AttrUsage_name_7[_AttrUsage_index_7[i]:_AttrUsage_index_7[i+1]] return _AttrUsage_name_6[_AttrUsage_index_6[i]:_AttrUsage_index_6[i+1]]
default: default:
return "AttrUsage(" + strconv.FormatInt(int64(i), 10) + ")" return "AttrUsage(" + strconv.FormatInt(int64(i), 10) + ")"
} }

View file

@ -19,6 +19,7 @@ func NewClaimTX(claim *ClaimTX) *Transaction {
Nonce: rand.Uint32(), Nonce: rand.Uint32(),
Data: claim, Data: claim,
Attributes: []Attribute{}, Attributes: []Attribute{},
Cosigners: []Cosigner{},
Inputs: []Input{}, Inputs: []Input{},
Outputs: []Output{}, Outputs: []Output{},
Scripts: []Witness{}, Scripts: []Witness{},

View file

@ -18,6 +18,7 @@ func NewContractTX() *Transaction {
Nonce: rand.Uint32(), Nonce: rand.Uint32(),
Data: &ContractTX{}, Data: &ContractTX{},
Attributes: []Attribute{}, Attributes: []Attribute{},
Cosigners: []Cosigner{},
Inputs: []Input{}, Inputs: []Input{},
Outputs: []Output{}, Outputs: []Output{},
Scripts: []Witness{}, Scripts: []Witness{},

View file

@ -0,0 +1,42 @@
package transaction
import (
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/util"
)
// The maximum number of AllowedContracts or AllowedGroups
const maxSubitems = 16
// Cosigner implements a Transaction cosigner.
type Cosigner struct {
Account util.Uint160 `json:"account"`
Scopes WitnessScope `json:"scopes"`
AllowedContracts []util.Uint160 `json:"allowedContracts,omitempty"`
AllowedGroups []*keys.PublicKey `json:"allowedGroups,omitempty"`
}
// EncodeBinary implements Serializable interface.
func (c *Cosigner) EncodeBinary(bw *io.BinWriter) {
bw.WriteBytes(c.Account[:])
bw.WriteB(byte(c.Scopes))
if c.Scopes&CustomContracts != 0 {
bw.WriteArray(c.AllowedContracts)
}
if c.Scopes&CustomGroups != 0 {
bw.WriteArray(c.AllowedGroups)
}
}
// DecodeBinary implements Serializable interface.
func (c *Cosigner) DecodeBinary(br *io.BinReader) {
br.ReadBytes(c.Account[:])
c.Scopes = WitnessScope(br.ReadB())
if c.Scopes&CustomContracts != 0 {
br.ReadArray(&c.AllowedContracts, maxSubitems)
}
if c.Scopes&CustomGroups != 0 {
br.ReadArray(&c.AllowedGroups, maxSubitems)
}
}

View file

@ -0,0 +1,28 @@
package transaction
import (
"testing"
"github.com/nspcc-dev/neo-go/pkg/internal/testserdes"
"github.com/nspcc-dev/neo-go/pkg/util"
)
func TestCosignerEncodeDecode(t *testing.T) {
expected := &Cosigner{
Account: util.Uint160{1, 2, 3, 4, 5},
Scopes: CustomContracts,
AllowedContracts: []util.Uint160{{1, 2, 3, 4}, {6, 7, 8, 9}},
}
actual := &Cosigner{}
testserdes.EncodeDecodeBinary(t, expected, actual)
}
func TestCosignerMarshallUnmarshallJSON(t *testing.T) {
expected := &Cosigner{
Account: util.Uint160{1, 2, 3, 4, 5},
Scopes: CustomContracts,
AllowedContracts: []util.Uint160{{1, 2, 3, 4}, {6, 7, 8, 9}},
}
actual := &Cosigner{}
testserdes.MarshalUnmarshalJSON(t, expected, actual)
}

View file

@ -31,6 +31,7 @@ func NewInvocationTX(script []byte, gas util.Fixed8) *Transaction {
Version: 1, Version: 1,
}, },
Attributes: []Attribute{}, Attributes: []Attribute{},
Cosigners: []Cosigner{},
Inputs: []Input{}, Inputs: []Input{},
Outputs: []Output{}, Outputs: []Output{},
Scripts: []Witness{}, Scripts: []Witness{},

View file

@ -18,6 +18,7 @@ func NewIssueTX() *Transaction {
Nonce: rand.Uint32(), Nonce: rand.Uint32(),
Data: &IssueTX{}, Data: &IssueTX{},
Attributes: []Attribute{}, Attributes: []Attribute{},
Cosigners: []Cosigner{},
Inputs: []Input{}, Inputs: []Input{},
Outputs: []Output{}, Outputs: []Output{},
Scripts: []Witness{}, Scripts: []Witness{},

View file

@ -39,6 +39,7 @@ func NewRegisterTX(register *RegisterTX) *Transaction {
Nonce: rand.Uint32(), Nonce: rand.Uint32(),
Data: register, Data: register,
Attributes: []Attribute{}, Attributes: []Attribute{},
Cosigners: []Cosigner{},
Inputs: []Input{}, Inputs: []Input{},
Outputs: []Output{}, Outputs: []Output{},
Scripts: []Witness{}, Scripts: []Witness{},
@ -57,7 +58,7 @@ func (tx *RegisterTX) DecodeBinary(br *io.BinReader) {
tx.Owner.DecodeBinary(br) tx.Owner.DecodeBinary(br)
br.ReadBytes(tx.Admin[:]) tx.Admin.DecodeBinary(br)
} }
// EncodeBinary implements Serializable interface. // EncodeBinary implements Serializable interface.
@ -67,7 +68,7 @@ func (tx *RegisterTX) EncodeBinary(bw *io.BinWriter) {
tx.Amount.EncodeBinary(bw) tx.Amount.EncodeBinary(bw)
bw.WriteB(byte(tx.Precision)) bw.WriteB(byte(tx.Precision))
bw.WriteBytes(tx.Owner.Bytes()) bw.WriteBytes(tx.Owner.Bytes())
bw.WriteBytes(tx.Admin[:]) tx.Admin.EncodeBinary(bw)
} }
// registeredAsset is a wrapper for RegisterTransaction // registeredAsset is a wrapper for RegisterTransaction

View file

@ -19,6 +19,9 @@ const (
// MaxValidUntilBlockIncrement is the upper increment size of blockhain height in blocs after // MaxValidUntilBlockIncrement is the upper increment size of blockhain height in blocs after
// exceeding that a transaction should fail validation. It is set to be 2102400. // exceeding that a transaction should fail validation. It is set to be 2102400.
MaxValidUntilBlockIncrement = 2102400 MaxValidUntilBlockIncrement = 2102400
// MaxCosigners is maximum number of cosigners that can be contained within a transaction.
// It is set to be 16.
MaxCosigners = 16
) )
// Transaction is a process recorded in the NEO blockchain. // Transaction is a process recorded in the NEO blockchain.
@ -46,6 +49,9 @@ type Transaction struct {
// Transaction attributes. // Transaction attributes.
Attributes []Attribute Attributes []Attribute
// Transaction cosigners (not include Sender).
Cosigners []Cosigner
// The inputs of the transaction. // The inputs of the transaction.
Inputs []Input Inputs []Input
@ -107,25 +113,28 @@ func (t *Transaction) AddInput(in *Input) {
t.Inputs = append(t.Inputs, *in) t.Inputs = append(t.Inputs, *in)
} }
// AddVerificationHash adds a script attribute for transaction verification.
func (t *Transaction) AddVerificationHash(addr util.Uint160) {
t.Attributes = append(t.Attributes, Attribute{
Usage: Script,
Data: addr.BytesBE(),
})
}
// DecodeBinary implements Serializable interface. // DecodeBinary implements Serializable interface.
func (t *Transaction) DecodeBinary(br *io.BinReader) { func (t *Transaction) DecodeBinary(br *io.BinReader) {
t.Type = TXType(br.ReadB()) t.Type = TXType(br.ReadB())
t.Version = uint8(br.ReadB()) t.Version = uint8(br.ReadB())
t.Nonce = br.ReadU32LE() t.Nonce = br.ReadU32LE()
br.ReadBytes(t.Sender[:]) t.Sender.DecodeBinary(br)
t.ValidUntilBlock = br.ReadU32LE() t.ValidUntilBlock = br.ReadU32LE()
t.decodeData(br) t.decodeData(br)
br.ReadArray(&t.Attributes) br.ReadArray(&t.Attributes)
br.ReadArray(&t.Cosigners, MaxCosigners)
for i := 0; i < len(t.Cosigners); i++ {
for j := i + 1; j < len(t.Cosigners); j++ {
if t.Cosigners[i].Account.Equals(t.Cosigners[j].Account) {
br.Err = errors.New("transaction cosigners should be unique")
return
}
}
}
br.ReadArray(&t.Inputs) br.ReadArray(&t.Inputs)
br.ReadArray(&t.Outputs) br.ReadArray(&t.Outputs)
for i := range t.Outputs { for i := range t.Outputs {
@ -182,7 +191,7 @@ func (t *Transaction) encodeHashableFields(bw *io.BinWriter) {
bw.WriteB(byte(t.Type)) bw.WriteB(byte(t.Type))
bw.WriteB(byte(t.Version)) bw.WriteB(byte(t.Version))
bw.WriteU32LE(t.Nonce) bw.WriteU32LE(t.Nonce)
bw.WriteBytes(t.Sender[:]) t.Sender.EncodeBinary(bw)
bw.WriteU32LE(t.ValidUntilBlock) bw.WriteU32LE(t.ValidUntilBlock)
// Underlying TXer. // Underlying TXer.
@ -193,6 +202,9 @@ func (t *Transaction) encodeHashableFields(bw *io.BinWriter) {
// Attributes // Attributes
bw.WriteArray(t.Attributes) bw.WriteArray(t.Attributes)
// Cosigners
bw.WriteArray(t.Cosigners)
// Inputs // Inputs
bw.WriteArray(t.Inputs) bw.WriteArray(t.Inputs)
@ -267,6 +279,7 @@ type transactionJSON struct {
Sender string `json:"sender"` Sender string `json:"sender"`
ValidUntilBlock uint32 `json:"valid_until_block"` ValidUntilBlock uint32 `json:"valid_until_block"`
Attributes []Attribute `json:"attributes"` Attributes []Attribute `json:"attributes"`
Cosigners []Cosigner `json:"cosigners"`
Inputs []Input `json:"vin"` Inputs []Input `json:"vin"`
Outputs []Output `json:"vout"` Outputs []Output `json:"vout"`
Scripts []Witness `json:"scripts"` Scripts []Witness `json:"scripts"`
@ -288,6 +301,7 @@ func (t *Transaction) MarshalJSON() ([]byte, error) {
Sender: address.Uint160ToString(t.Sender), Sender: address.Uint160ToString(t.Sender),
ValidUntilBlock: t.ValidUntilBlock, ValidUntilBlock: t.ValidUntilBlock,
Attributes: t.Attributes, Attributes: t.Attributes,
Cosigners: t.Cosigners,
Inputs: t.Inputs, Inputs: t.Inputs,
Outputs: t.Outputs, Outputs: t.Outputs,
Scripts: t.Scripts, Scripts: t.Scripts,
@ -323,6 +337,7 @@ func (t *Transaction) UnmarshalJSON(data []byte) error {
t.Nonce = tx.Nonce t.Nonce = tx.Nonce
t.ValidUntilBlock = tx.ValidUntilBlock t.ValidUntilBlock = tx.ValidUntilBlock
t.Attributes = tx.Attributes t.Attributes = tx.Attributes
t.Cosigners = tx.Cosigners
t.Inputs = tx.Inputs t.Inputs = tx.Inputs
t.Outputs = tx.Outputs t.Outputs = tx.Outputs
t.Scripts = tx.Scripts t.Scripts = tx.Scripts

View file

@ -0,0 +1,19 @@
package transaction
// WitnessScope represents set of witness flags for Transaction cosigner.
type WitnessScope byte
const (
// Global allows this witness in all contexts (default Neo2 behavior).
// This cannot be combined with other flags.
Global WitnessScope = 0x00
// CalledByEntry means that this condition must hold: EntryScriptHash == CallingScriptHash.
// No params is needed, as the witness/permission/signature given on first invocation will
// automatically expire if entering deeper internal invokes. This can be default safe
// choice for native NEO/GAS (previously used on Neo 2 as "attach" mode).
CalledByEntry WitnessScope = 0x01
// CustomContracts define custom hash for contract-specific.
CustomContracts WitnessScope = 0x10
// CustomGroups define custom pubkey for group members.
CustomGroups WitnessScope = 0x20
)

View file

@ -20,7 +20,7 @@ func TestGenesisBlockMainNet(t *testing.T) {
// have been changed. Consequently, hash of the genesis block has been changed. // have been changed. Consequently, hash of the genesis block has been changed.
// Update expected genesis block hash for better times. // Update expected genesis block hash for better times.
// Old hash is "d42561e3d30e15be6400b6df2f328e02d2bf6354c41dce433bc57687c82144bf" // Old hash is "d42561e3d30e15be6400b6df2f328e02d2bf6354c41dce433bc57687c82144bf"
expect := "16ffda4cc6a9b0a0ca63f1571f7724418c43f014e2eb4e8614b4938ccf0f20f6" expect := "8eb36fe47f07a795a1783a9f066603db66c5b76cf878650b1e137c114f46c0cc"
assert.Equal(t, expect, block.Hash().StringLE()) assert.Equal(t, expect, block.Hash().StringLE())
} }
@ -47,7 +47,7 @@ func TestUtilityTokenTX(t *testing.T) {
//TODO: After we added Nonce field to transaction.Transaction, UtilityTockenTx hash //TODO: After we added Nonce field to transaction.Transaction, UtilityTockenTx hash
// has been changed. Update it for better times. // has been changed. Update it for better times.
// Old hash is "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7" // Old hash is "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"
expect := "b16384a950ed01ed5fc15c03fe7b98228871cb43b1bc22d67029449fc854d104" expect := "8ef63ccd6f4ea20a93e7f4e84b2d43f778077612b241d617e42e1750cca4f2c5"
assert.Equal(t, expect, UtilityTokenID().StringLE()) assert.Equal(t, expect, UtilityTokenID().StringLE())
} }
@ -55,6 +55,6 @@ func TestGoverningTokenTX(t *testing.T) {
//TODO: After we added Nonce field to transaction.Transaction, GoveringTockenTx hash //TODO: After we added Nonce field to transaction.Transaction, GoveringTockenTx hash
// has been changed. Update it for better times. // has been changed. Update it for better times.
// Old hash is "c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b" // Old hash is "c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b"
expect := "7a37715546c6cfa5bac8d7f7e87c667a1e5a6ba0601238be475ab8c79a5abcf5" expect := "7dc12f8835b085d468ddbab3f7152c0e2f5679b5815b2028685abb4608e7b7dc"
assert.Equal(t, expect, GoverningTokenID().StringLE()) assert.Equal(t, expect, GoverningTokenID().StringLE())
} }

View file

@ -108,22 +108,13 @@ func (c *Client) TransferNEP5(acc *wallet.Account, to util.Uint160, token *walle
emit.Opcode(w.BinWriter, opcode.THROWIFNOT) emit.Opcode(w.BinWriter, opcode.THROWIFNOT)
tx := transaction.NewInvocationTX(w.Bytes(), gas) tx := transaction.NewInvocationTX(w.Bytes(), gas)
tx.Attributes = append(tx.Attributes, transaction.Attribute{ tx.Sender = from
Usage: transaction.Script,
Data: from.BytesBE(),
})
tx.ValidUntilBlock, err = c.CalculateValidUntilBlock() tx.ValidUntilBlock, err = c.CalculateValidUntilBlock()
if err != nil { if err != nil {
return util.Uint256{}, fmt.Errorf("can't calculate validUntilBlock: %v", err) return util.Uint256{}, fmt.Errorf("can't calculate validUntilBlock: %v", err)
} }
addr, err := address.StringToUint160(acc.Address)
if err != nil {
return util.Uint256{}, fmt.Errorf("failed to get address: %v", err)
}
tx.Sender = addr
if err := request.AddInputsAndUnspentsToTx(tx, acc.Address, core.UtilityTokenID(), gas, c); err != nil { if err := request.AddInputsAndUnspentsToTx(tx, acc.Address, core.UtilityTokenID(), gas, c); err != nil {
return util.Uint256{}, fmt.Errorf("can't add GAS to transaction: %v", err) return util.Uint256{}, fmt.Errorf("can't add GAS to transaction: %v", err)
} }

View file

@ -38,7 +38,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
invoke: func(c *Client) (interface{}, error) { invoke: func(c *Client) (interface{}, error) {
return c.GetAccountState("") return c.GetAccountState("")
}, },
serverResponse: `{"jsonrpc":"2.0","id": 1,"result":{"version":0,"script_hash":"0x1179716da2e9523d153a35fb3ad10c561b1e5b1a","frozen":false,"votes":[],"balances":[{"asset":"0x7a37715546c6cfa5bac8d7f7e87c667a1e5a6ba0601238be475ab8c79a5abcf5","value":"94"}]}}`, serverResponse: `{"jsonrpc":"2.0","id": 1,"result":{"version":0,"script_hash":"0x1179716da2e9523d153a35fb3ad10c561b1e5b1a","frozen":false,"votes":[],"balances":[{"asset":"0x7dc12f8835b085d468ddbab3f7152c0e2f5679b5815b2028685abb4608e7b7dc","value":"94"}]}}`,
result: func(c *Client) interface{} { result: func(c *Client) interface{} {
scriptHash, err := util.Uint160DecodeStringLE("1179716da2e9523d153a35fb3ad10c561b1e5b1a") scriptHash, err := util.Uint160DecodeStringLE("1179716da2e9523d153a35fb3ad10c561b1e5b1a")
if err != nil { if err != nil {
@ -96,7 +96,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
invoke: func(c *Client) (interface{}, error) { invoke: func(c *Client) (interface{}, error) {
return c.GetAssetState(util.Uint256{}) return c.GetAssetState(util.Uint256{})
}, },
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"id":"0x7a37715546c6cfa5bac8d7f7e87c667a1e5a6ba0601238be475ab8c79a5abcf5","type":0,"name":"NEO","amount":"100000000","available":"100000000","precision":0,"owner":"00","admin":"Abf2qMs1pzQb8kYk9RuxtUb9jtRKJVuBJt","issuer":"AFmseVrdL9f9oyCzZefL9tG6UbvhPbdYzM","expiration":4000000,"is_frozen":false}}`, serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"id":"0x7dc12f8835b085d468ddbab3f7152c0e2f5679b5815b2028685abb4608e7b7dc","type":0,"name":"NEO","amount":"100000000","available":"100000000","precision":0,"owner":"00","admin":"Abf2qMs1pzQb8kYk9RuxtUb9jtRKJVuBJt","issuer":"AFmseVrdL9f9oyCzZefL9tG6UbvhPbdYzM","expiration":4000000,"is_frozen":false}}`,
result: func(c *Client) interface{} { result: func(c *Client) interface{} {
return &result.AssetState{ return &result.AssetState{
ID: core.GoverningTokenID(), ID: core.GoverningTokenID(),
@ -134,45 +134,45 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
{ {
name: "byIndex_positive", name: "byIndex_positive",
invoke: func(c *Client) (interface{}, error) { invoke: func(c *Client) (interface{}, error) {
return c.GetBlockByIndex(5) return c.GetBlockByIndex(202)
}, },
serverResponse: `{"id":1,"jsonrpc":"2.0","result":"000000005b25cf560918b13cc7a10071cf616426a103d58498171567aaa116af69179aa3c05c359ade4316392d69d5ca01f8860f8bdebbe1a8f752edd68d9a6a2d204fe752cca25e0000000005000000e903736ceceeceae1806eee0e3ec61e7cce476ce01fd08010c407f70912fdcd0ce9a70963110eb39ef73ed02512ea63bc1c55577474df8f364a874999b16666888f8a3ca69873054a31ab42dd1fee1ed8605ff65edb39797e34e0c40d59459cb1cc8270d9a813aa444852a5d7714aed0b25373ec07549349c6db32b6c8c2e43cdd7b1ad2a4154cb5ff1222d9d7010877016a59775abdd2c08f253c850c4048b65cb58e0d58f2dd0f5db3b195c5784933d6179d8317217ba46f9deac9a667bffb1afd0dd86bdae6267af4c2605f7401849a0b980fcecfeae95df7f5ca6e140c406d9b1942c5aa80070ad484b3dd76421a3f46327670bea94e3ecb3a94eef8843a75195b55872a7c503acb01ab368cdaa11a9e5fb80d55bbd6b861233ba84edcf894130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b683073b3bb020057040000000000008000d5040000e903736ceceeceae1806eee0e3ec61e7cce476ce0500000000000001fd08010c40ae62dadb95b21628921adb9ee14e8c1f20d68b9d6ffba218f8e403af70126449435a53525cd6f1d1c871e64a91025866cd8a413ec70e3cde0ddf62931aa22baa0c40c3e09fc8ad46789d645240634ef40bd180e0dba75beae07a37dc2e899b68b4b3b3a67d076ea9444ed143dbb3264702914a4662ff95859d32c9b5bb5bdc9ec1d60c40b2511115482bd860d85f57103fb8cce2cb45ba8461d4e164685b426cb5df1525e6f09dd7aa23db0fec2241bde7c925e22f3cba3824ea461455515dc2a1455f5e0c40c0aee2528e7fd75a0434f4d67da58507e8d349ca29e01e7eafa9f283e8b8c05d06587db7234f111730c14c8a4bfb7a1ffd9493b9696c71e68966c6bf4a832ab794130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b683073b3bb"}`, serverResponse: `{"id":1,"jsonrpc":"2.0","result":"0000000039f06b13aa6ee24d37db9b7868533b7988a6fb1e59c3fa6677dab7fd1f51326405f4fa24f061ffa04ab3127994530ee04c5e578a7b8109133dd142b6a31192cd9debaa5e00000000ca000000e903736ceceeceae1806eee0e3ec61e7cce476ce01fd08010c404617b6788538326383015c44ffddd4a05a4e200b65a26fc84234ae8b1e28ef27b7f139dc498c58071193d530ba83081701290eba8f7108397499f5556c16e3780c402131f2bdcc494c73a379e86c46f9e9fe9899a05b23928926b1eaaf816928e160fe971b82263aa1e7efa5f7e46bf99de735fc4fc5aeb81edfdc6a9b2e9fcfa1000c408cbae1582bb9d82de9ff030b8c729737f2157844c0ca29edcdbfed1dd5e2473e0061f0dc29412477417e2c1f7c55443f11b9bd6e0d0856d1ec00240be1b9b9a70c4034d1055531cf0522ac7e5dcd817cf3cd86997ae38da806dc789b1f16eb0005f00b9bc29f7372bb43a8fed040c6763b162c8a0d8e2d0b2d7476e22e0b2b77601094130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b683073b3bb0200570400000000000080000300000075a94799633ed955dd85a8af314a5b435ab51903b0040000000001f6c31d4dd9edf7aea24168885ae82dc574d25e35a9b9b3063f3ea44226a3081f000001dcb7e70846bb5a6828205b81b579562f0e2c15f7b3badd68d485b035882fc17d00184a27db86230075a94799633ed955dd85a8af314a5b435ab5190301420c40bfce1ead7d53339440bb29745eed4ad9840875de4f970950065291e7a14cbd249bfbf777d9a997c5e00bbc08e8ce9fdd2cd13c45c3585b4939599ff84c6149ff290c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b680a906ad4"}`,
result: func(c *Client) interface{} { return &block.Block{} }, result: func(c *Client) interface{} { return &block.Block{} },
check: func(t *testing.T, c *Client, result interface{}) { check: func(t *testing.T, c *Client, result interface{}) {
res, ok := result.(*block.Block) res, ok := result.(*block.Block)
require.True(t, ok) require.True(t, ok)
assert.Equal(t, uint32(0), res.Version) assert.Equal(t, uint32(0), res.Version)
assert.Equal(t, "81b74dcfc3a42ef331eec2486231aba300ec9a95b024b0151214c76305d409bb", res.Hash().StringLE()) assert.Equal(t, "75ab743487ce969792a4cb3c235d9ea5ff97af42b2646183c981437a077fbc45", res.Hash().StringLE())
assert.Equal(t, "a39a1769af16a1aa6715179884d503a1266461cf7100a1c73cb1180956cf255b", res.PrevHash.StringLE()) assert.Equal(t, "6432511ffdb7da7766fac3591efba688793b5368789bdb374de26eaa136bf039", res.PrevHash.StringLE())
assert.Equal(t, "e74f202d6a9a8dd6ed52f7a8e1bbde8b0f86f801cad5692d391643de9a355cc0", res.MerkleRoot.StringLE()) assert.Equal(t, "cd9211a3b642d13d1309817b8a575e4ce00e53947912b34aa0ff61f024faf405", res.MerkleRoot.StringLE())
assert.Equal(t, 1, len(res.Transactions)) assert.Equal(t, 1, len(res.Transactions))
assert.Equal(t, "69d95138263dc54c07826ef1d76f9eb32ff6bf7fc3fc3281194b46ec7683ceb3", res.Transactions[0].Hash().StringLE()) assert.Equal(t, "115975f7d1e9c4577ec839c6ea4ae32727616284e2d7cdc5a49e4e421b32e505", res.Transactions[0].Hash().StringLE())
}, },
}, },
{ {
name: "byIndex_verbose_positive", name: "byIndex_verbose_positive",
invoke: func(c *Client) (i interface{}, err error) { invoke: func(c *Client) (i interface{}, err error) {
return c.GetBlockByIndexVerbose(5) return c.GetBlockByIndexVerbose(202)
}, },
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"hash":"0x81b74dcfc3a42ef331eec2486231aba300ec9a95b024b0151214c76305d409bb","size":977,"version":0,"nextblockhash":"0xd08e408909ae24de3470a3f231f966e15719910cb7b1a30f356b3e784c1ddf6a","previousblockhash":"0xa39a1769af16a1aa6715179884d503a1266461cf7100a1c73cb1180956cf255b","merkleroot":"0xe74f202d6a9a8dd6ed52f7a8e1bbde8b0f86f801cad5692d391643de9a355cc0","time":1587727442,"index":5,"consensus_data":{"primary":0,"nonce":"0000000000000457"},"nextconsensus":"Ad1wDxzcRiRSryvJobNV211Tv7UUiziPXy","confirmations":203,"script":{"invocation":"0c407f70912fdcd0ce9a70963110eb39ef73ed02512ea63bc1c55577474df8f364a874999b16666888f8a3ca69873054a31ab42dd1fee1ed8605ff65edb39797e34e0c40d59459cb1cc8270d9a813aa444852a5d7714aed0b25373ec07549349c6db32b6c8c2e43cdd7b1ad2a4154cb5ff1222d9d7010877016a59775abdd2c08f253c850c4048b65cb58e0d58f2dd0f5db3b195c5784933d6179d8317217ba46f9deac9a667bffb1afd0dd86bdae6267af4c2605f7401849a0b980fcecfeae95df7f5ca6e140c406d9b1942c5aa80070ad484b3dd76421a3f46327670bea94e3ecb3a94eef8843a75195b55872a7c503acb01ab368cdaa11a9e5fb80d55bbd6b861233ba84edcf8","verification":"130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b683073b3bb"},"tx":[{"sys_fee":"0","net_fee":"0","txid":"0x69d95138263dc54c07826ef1d76f9eb32ff6bf7fc3fc3281194b46ec7683ceb3","size":450,"type":"ContractTransaction","version":0,"nonce":1237,"sender":"Ad1wDxzcRiRSryvJobNV211Tv7UUiziPXy","valid_until_block":5,"attributes":[],"vin":[],"vout":[],"scripts":[{"invocation":"0c40ae62dadb95b21628921adb9ee14e8c1f20d68b9d6ffba218f8e403af70126449435a53525cd6f1d1c871e64a91025866cd8a413ec70e3cde0ddf62931aa22baa0c40c3e09fc8ad46789d645240634ef40bd180e0dba75beae07a37dc2e899b68b4b3b3a67d076ea9444ed143dbb3264702914a4662ff95859d32c9b5bb5bdc9ec1d60c40b2511115482bd860d85f57103fb8cce2cb45ba8461d4e164685b426cb5df1525e6f09dd7aa23db0fec2241bde7c925e22f3cba3824ea461455515dc2a1455f5e0c40c0aee2528e7fd75a0434f4d67da58507e8d349ca29e01e7eafa9f283e8b8c05d06587db7234f111730c14c8a4bfb7a1ffd9493b9696c71e68966c6bf4a832ab7","verification":"130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b683073b3bb"}]}]}}`, serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"hash":"0x75ab743487ce969792a4cb3c235d9ea5ff97af42b2646183c981437a077fbc45","size":765,"version":0,"nextblockhash":"0xec27fed4ff8ad6a87bdd29aecee43e9ecb4e336321dac875d0b14d2aab1c5798","previousblockhash":"0x6432511ffdb7da7766fac3591efba688793b5368789bdb374de26eaa136bf039","merkleroot":"0xcd9211a3b642d13d1309817b8a575e4ce00e53947912b34aa0ff61f024faf405","time":1588259741,"index":202,"consensus_data":{"primary":0,"nonce":"0000000000000457"},"nextconsensus":"Ad1wDxzcRiRSryvJobNV211Tv7UUiziPXy","confirmations":6,"script":{"invocation":"0c404617b6788538326383015c44ffddd4a05a4e200b65a26fc84234ae8b1e28ef27b7f139dc498c58071193d530ba83081701290eba8f7108397499f5556c16e3780c402131f2bdcc494c73a379e86c46f9e9fe9899a05b23928926b1eaaf816928e160fe971b82263aa1e7efa5f7e46bf99de735fc4fc5aeb81edfdc6a9b2e9fcfa1000c408cbae1582bb9d82de9ff030b8c729737f2157844c0ca29edcdbfed1dd5e2473e0061f0dc29412477417e2c1f7c55443f11b9bd6e0d0856d1ec00240be1b9b9a70c4034d1055531cf0522ac7e5dcd817cf3cd86997ae38da806dc789b1f16eb0005f00b9bc29f7372bb43a8fed040c6763b162c8a0d8e2d0b2d7476e22e0b2b776010","verification":"130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b683073b3bb"},"tx":[{"sys_fee":"0","net_fee":"0","txid":"0x115975f7d1e9c4577ec839c6ea4ae32727616284e2d7cdc5a49e4e421b32e505","size":238,"type":"ContractTransaction","version":0,"nonce":3,"sender":"ASW1VhcukJRrukCXRipY4BE9d9zy4mAYsR","valid_until_block":1200,"attributes":[],"cosigners":[],"vin":[{"txid":"0x1f08a32642a43e3f06b3b9a9355ed274c52de85a886841a2aef7edd94d1dc3f6","vout":0}],"vout":[{"address":"ASW1VhcukJRrukCXRipY4BE9d9zy4mAYsR","asset":"0x7dc12f8835b085d468ddbab3f7152c0e2f5679b5815b2028685abb4608e7b7dc","n":0,"value":"99999000"}],"scripts":[{"invocation":"0c40bfce1ead7d53339440bb29745eed4ad9840875de4f970950065291e7a14cbd249bfbf777d9a997c5e00bbc08e8ce9fdd2cd13c45c3585b4939599ff84c6149ff","verification":"0c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b680a906ad4"}]}]}}`,
result: func(c *Client) interface{} { result: func(c *Client) interface{} {
hash, err := util.Uint256DecodeStringLE("81b74dcfc3a42ef331eec2486231aba300ec9a95b024b0151214c76305d409bb") hash, err := util.Uint256DecodeStringLE("75ab743487ce969792a4cb3c235d9ea5ff97af42b2646183c981437a077fbc45")
if err != nil { if err != nil {
panic(err) panic(err)
} }
nextBlockHash, err := util.Uint256DecodeStringLE("d08e408909ae24de3470a3f231f966e15719910cb7b1a30f356b3e784c1ddf6a") nextBlockHash, err := util.Uint256DecodeStringLE("ec27fed4ff8ad6a87bdd29aecee43e9ecb4e336321dac875d0b14d2aab1c5798")
if err != nil { if err != nil {
panic(err) panic(err)
} }
prevBlockHash, err := util.Uint256DecodeStringLE("a39a1769af16a1aa6715179884d503a1266461cf7100a1c73cb1180956cf255b") prevBlockHash, err := util.Uint256DecodeStringLE("6432511ffdb7da7766fac3591efba688793b5368789bdb374de26eaa136bf039")
if err != nil { if err != nil {
panic(err) panic(err)
} }
merkleRoot, err := util.Uint256DecodeStringLE("e74f202d6a9a8dd6ed52f7a8e1bbde8b0f86f801cad5692d391643de9a355cc0") merkleRoot, err := util.Uint256DecodeStringLE("cd9211a3b642d13d1309817b8a575e4ce00e53947912b34aa0ff61f024faf405")
if err != nil { if err != nil {
panic(err) panic(err)
} }
invScript, err := hex.DecodeString("0c407f70912fdcd0ce9a70963110eb39ef73ed02512ea63bc1c55577474df8f364a874999b16666888f8a3ca69873054a31ab42dd1fee1ed8605ff65edb39797e34e0c40d59459cb1cc8270d9a813aa444852a5d7714aed0b25373ec07549349c6db32b6c8c2e43cdd7b1ad2a4154cb5ff1222d9d7010877016a59775abdd2c08f253c850c4048b65cb58e0d58f2dd0f5db3b195c5784933d6179d8317217ba46f9deac9a667bffb1afd0dd86bdae6267af4c2605f7401849a0b980fcecfeae95df7f5ca6e140c406d9b1942c5aa80070ad484b3dd76421a3f46327670bea94e3ecb3a94eef8843a75195b55872a7c503acb01ab368cdaa11a9e5fb80d55bbd6b861233ba84edcf8") invScript, err := hex.DecodeString("0c404617b6788538326383015c44ffddd4a05a4e200b65a26fc84234ae8b1e28ef27b7f139dc498c58071193d530ba83081701290eba8f7108397499f5556c16e3780c402131f2bdcc494c73a379e86c46f9e9fe9899a05b23928926b1eaaf816928e160fe971b82263aa1e7efa5f7e46bf99de735fc4fc5aeb81edfdc6a9b2e9fcfa1000c408cbae1582bb9d82de9ff030b8c729737f2157844c0ca29edcdbfed1dd5e2473e0061f0dc29412477417e2c1f7c55443f11b9bd6e0d0856d1ec00240be1b9b9a70c4034d1055531cf0522ac7e5dcd817cf3cd86997ae38da806dc789b1f16eb0005f00b9bc29f7372bb43a8fed040c6763b162c8a0d8e2d0b2d7476e22e0b2b776010")
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -180,21 +180,29 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
if err != nil { if err != nil {
panic(err) panic(err)
} }
sender, err := address.StringToUint160("Ad1wDxzcRiRSryvJobNV211Tv7UUiziPXy") sender, err := address.StringToUint160("ASW1VhcukJRrukCXRipY4BE9d9zy4mAYsR")
if err != nil { if err != nil {
panic(err) panic(err)
} }
txInvScript, err := hex.DecodeString("0c40ae62dadb95b21628921adb9ee14e8c1f20d68b9d6ffba218f8e403af70126449435a53525cd6f1d1c871e64a91025866cd8a413ec70e3cde0ddf62931aa22baa0c40c3e09fc8ad46789d645240634ef40bd180e0dba75beae07a37dc2e899b68b4b3b3a67d076ea9444ed143dbb3264702914a4662ff95859d32c9b5bb5bdc9ec1d60c40b2511115482bd860d85f57103fb8cce2cb45ba8461d4e164685b426cb5df1525e6f09dd7aa23db0fec2241bde7c925e22f3cba3824ea461455515dc2a1455f5e0c40c0aee2528e7fd75a0434f4d67da58507e8d349ca29e01e7eafa9f283e8b8c05d06587db7234f111730c14c8a4bfb7a1ffd9493b9696c71e68966c6bf4a832ab7") txInvScript, err := hex.DecodeString("0c40bfce1ead7d53339440bb29745eed4ad9840875de4f970950065291e7a14cbd249bfbf777d9a997c5e00bbc08e8ce9fdd2cd13c45c3585b4939599ff84c6149ff")
if err != nil { if err != nil {
panic(err) panic(err)
} }
txVerifScript, err := hex.DecodeString("130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b683073b3bb") txVerifScript, err := hex.DecodeString("0c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b680a906ad4")
if err != nil {
panic(err)
}
vin, err := util.Uint256DecodeStringLE("1f08a32642a43e3f06b3b9a9355ed274c52de85a886841a2aef7edd94d1dc3f6")
if err != nil {
panic(err)
}
outAddress, err := address.StringToUint160("ASW1VhcukJRrukCXRipY4BE9d9zy4mAYsR")
if err != nil { if err != nil {
panic(err) panic(err)
} }
tx := transaction.NewContractTX() tx := transaction.NewContractTX()
tx.Nonce = 1237 tx.Nonce = 3
tx.ValidUntilBlock = 5 tx.ValidUntilBlock = 1200
tx.Sender = sender tx.Sender = sender
tx.Scripts = []transaction.Witness{ tx.Scripts = []transaction.Witness{
{ {
@ -202,19 +210,34 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
VerificationScript: txVerifScript, VerificationScript: txVerifScript,
}, },
} }
tx.Inputs = []transaction.Input{
{
PrevHash: vin,
PrevIndex: 0,
},
}
tx.Outputs = []transaction.Output{
{
AssetID: core.GoverningTokenID(),
Amount: util.Fixed8FromInt64(99999000),
ScriptHash: outAddress,
Position: 0,
},
}
// Update hashes for correct result comparison. // Update hashes for correct result comparison.
_ = tx.Hash() _ = tx.Hash()
return &result.Block{ return &result.Block{
Hash: hash, Hash: hash,
Size: 977, Size: 765,
Version: 0, Version: 0,
NextBlockHash: &nextBlockHash, NextBlockHash: &nextBlockHash,
PreviousBlockHash: prevBlockHash, PreviousBlockHash: prevBlockHash,
MerkleRoot: merkleRoot, MerkleRoot: merkleRoot,
Time: 1587727442, Time: 1588259741,
Index: 5, Index: 202,
NextConsensus: "Ad1wDxzcRiRSryvJobNV211Tv7UUiziPXy", NextConsensus: "Ad1wDxzcRiRSryvJobNV211Tv7UUiziPXy",
Confirmations: 203, Confirmations: 6,
ConsensusData: result.ConsensusData{ ConsensusData: result.ConsensusData{
PrimaryIndex: 0, PrimaryIndex: 0,
Nonce: "0000000000000457", Nonce: "0000000000000457",
@ -236,53 +259,53 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
{ {
name: "byHash_positive", name: "byHash_positive",
invoke: func(c *Client) (interface{}, error) { invoke: func(c *Client) (interface{}, error) {
hash, err := util.Uint256DecodeStringLE("e9f71b58764157f1e2c3e29f217e654b57956ee7c8a60496b03ea85e39084b42") hash, err := util.Uint256DecodeStringLE("45bc7f077a4381c9836164b242af97ffa59e5d233ccba4929796ce873474ab75")
if err != nil { if err != nil {
panic(err) panic(err)
} }
return c.GetBlockByHash(hash) return c.GetBlockByHash(hash)
}, },
serverResponse: `{"id":1,"jsonrpc":"2.0","result":"000000005b25cf560918b13cc7a10071cf616426a103d58498171567aaa116af69179aa3c05c359ade4316392d69d5ca01f8860f8bdebbe1a8f752edd68d9a6a2d204fe752cca25e0000000005000000e903736ceceeceae1806eee0e3ec61e7cce476ce01fd08010c407f70912fdcd0ce9a70963110eb39ef73ed02512ea63bc1c55577474df8f364a874999b16666888f8a3ca69873054a31ab42dd1fee1ed8605ff65edb39797e34e0c40d59459cb1cc8270d9a813aa444852a5d7714aed0b25373ec07549349c6db32b6c8c2e43cdd7b1ad2a4154cb5ff1222d9d7010877016a59775abdd2c08f253c850c4048b65cb58e0d58f2dd0f5db3b195c5784933d6179d8317217ba46f9deac9a667bffb1afd0dd86bdae6267af4c2605f7401849a0b980fcecfeae95df7f5ca6e140c406d9b1942c5aa80070ad484b3dd76421a3f46327670bea94e3ecb3a94eef8843a75195b55872a7c503acb01ab368cdaa11a9e5fb80d55bbd6b861233ba84edcf894130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b683073b3bb020057040000000000008000d5040000e903736ceceeceae1806eee0e3ec61e7cce476ce0500000000000001fd08010c40ae62dadb95b21628921adb9ee14e8c1f20d68b9d6ffba218f8e403af70126449435a53525cd6f1d1c871e64a91025866cd8a413ec70e3cde0ddf62931aa22baa0c40c3e09fc8ad46789d645240634ef40bd180e0dba75beae07a37dc2e899b68b4b3b3a67d076ea9444ed143dbb3264702914a4662ff95859d32c9b5bb5bdc9ec1d60c40b2511115482bd860d85f57103fb8cce2cb45ba8461d4e164685b426cb5df1525e6f09dd7aa23db0fec2241bde7c925e22f3cba3824ea461455515dc2a1455f5e0c40c0aee2528e7fd75a0434f4d67da58507e8d349ca29e01e7eafa9f283e8b8c05d06587db7234f111730c14c8a4bfb7a1ffd9493b9696c71e68966c6bf4a832ab794130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b683073b3bb"}`, serverResponse: `{"id":1,"jsonrpc":"2.0","result":"0000000039f06b13aa6ee24d37db9b7868533b7988a6fb1e59c3fa6677dab7fd1f51326405f4fa24f061ffa04ab3127994530ee04c5e578a7b8109133dd142b6a31192cd9debaa5e00000000ca000000e903736ceceeceae1806eee0e3ec61e7cce476ce01fd08010c404617b6788538326383015c44ffddd4a05a4e200b65a26fc84234ae8b1e28ef27b7f139dc498c58071193d530ba83081701290eba8f7108397499f5556c16e3780c402131f2bdcc494c73a379e86c46f9e9fe9899a05b23928926b1eaaf816928e160fe971b82263aa1e7efa5f7e46bf99de735fc4fc5aeb81edfdc6a9b2e9fcfa1000c408cbae1582bb9d82de9ff030b8c729737f2157844c0ca29edcdbfed1dd5e2473e0061f0dc29412477417e2c1f7c55443f11b9bd6e0d0856d1ec00240be1b9b9a70c4034d1055531cf0522ac7e5dcd817cf3cd86997ae38da806dc789b1f16eb0005f00b9bc29f7372bb43a8fed040c6763b162c8a0d8e2d0b2d7476e22e0b2b77601094130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b683073b3bb0200570400000000000080000300000075a94799633ed955dd85a8af314a5b435ab51903b0040000000001f6c31d4dd9edf7aea24168885ae82dc574d25e35a9b9b3063f3ea44226a3081f000001dcb7e70846bb5a6828205b81b579562f0e2c15f7b3badd68d485b035882fc17d00184a27db86230075a94799633ed955dd85a8af314a5b435ab5190301420c40bfce1ead7d53339440bb29745eed4ad9840875de4f970950065291e7a14cbd249bfbf777d9a997c5e00bbc08e8ce9fdd2cd13c45c3585b4939599ff84c6149ff290c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b680a906ad4"}`,
result: func(c *Client) interface{} { return &block.Block{} }, result: func(c *Client) interface{} { return &block.Block{} },
check: func(t *testing.T, c *Client, result interface{}) { check: func(t *testing.T, c *Client, result interface{}) {
res, ok := result.(*block.Block) res, ok := result.(*block.Block)
require.True(t, ok) require.True(t, ok)
assert.Equal(t, uint32(0), res.Version) assert.Equal(t, uint32(0), res.Version)
assert.Equal(t, "81b74dcfc3a42ef331eec2486231aba300ec9a95b024b0151214c76305d409bb", res.Hash().StringLE()) assert.Equal(t, "75ab743487ce969792a4cb3c235d9ea5ff97af42b2646183c981437a077fbc45", res.Hash().StringLE())
assert.Equal(t, "a39a1769af16a1aa6715179884d503a1266461cf7100a1c73cb1180956cf255b", res.PrevHash.StringLE()) assert.Equal(t, "6432511ffdb7da7766fac3591efba688793b5368789bdb374de26eaa136bf039", res.PrevHash.StringLE())
assert.Equal(t, "e74f202d6a9a8dd6ed52f7a8e1bbde8b0f86f801cad5692d391643de9a355cc0", res.MerkleRoot.StringLE()) assert.Equal(t, "cd9211a3b642d13d1309817b8a575e4ce00e53947912b34aa0ff61f024faf405", res.MerkleRoot.StringLE())
assert.Equal(t, 1, len(res.Transactions)) assert.Equal(t, 1, len(res.Transactions))
assert.Equal(t, "69d95138263dc54c07826ef1d76f9eb32ff6bf7fc3fc3281194b46ec7683ceb3", res.Transactions[0].Hash().StringLE()) assert.Equal(t, "115975f7d1e9c4577ec839c6ea4ae32727616284e2d7cdc5a49e4e421b32e505", res.Transactions[0].Hash().StringLE())
}, },
}, },
{ {
name: "byHash_verbose_positive", name: "byHash_verbose_positive",
invoke: func(c *Client) (i interface{}, err error) { invoke: func(c *Client) (i interface{}, err error) {
hash, err := util.Uint256DecodeStringLE("bb09d40563c7141215b024b0959aec00a3ab316248c2ee31f32ea4c3cf4db781") hash, err := util.Uint256DecodeStringLE("45bc7f077a4381c9836164b242af97ffa59e5d233ccba4929796ce873474ab75")
if err != nil { if err != nil {
panic(err) panic(err)
} }
return c.GetBlockByHashVerbose(hash) return c.GetBlockByHashVerbose(hash)
}, },
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"hash":"0x81b74dcfc3a42ef331eec2486231aba300ec9a95b024b0151214c76305d409bb","size":977,"version":0,"nextblockhash":"0xd08e408909ae24de3470a3f231f966e15719910cb7b1a30f356b3e784c1ddf6a","previousblockhash":"0xa39a1769af16a1aa6715179884d503a1266461cf7100a1c73cb1180956cf255b","merkleroot":"0xe74f202d6a9a8dd6ed52f7a8e1bbde8b0f86f801cad5692d391643de9a355cc0","time":1587727442,"index":5,"consensus_data":{"primary":0,"nonce":"0000000000000457"},"nextconsensus":"Ad1wDxzcRiRSryvJobNV211Tv7UUiziPXy","confirmations":203,"script":{"invocation":"0c407f70912fdcd0ce9a70963110eb39ef73ed02512ea63bc1c55577474df8f364a874999b16666888f8a3ca69873054a31ab42dd1fee1ed8605ff65edb39797e34e0c40d59459cb1cc8270d9a813aa444852a5d7714aed0b25373ec07549349c6db32b6c8c2e43cdd7b1ad2a4154cb5ff1222d9d7010877016a59775abdd2c08f253c850c4048b65cb58e0d58f2dd0f5db3b195c5784933d6179d8317217ba46f9deac9a667bffb1afd0dd86bdae6267af4c2605f7401849a0b980fcecfeae95df7f5ca6e140c406d9b1942c5aa80070ad484b3dd76421a3f46327670bea94e3ecb3a94eef8843a75195b55872a7c503acb01ab368cdaa11a9e5fb80d55bbd6b861233ba84edcf8","verification":"130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b683073b3bb"},"tx":[{"sys_fee":"0","net_fee":"0","txid":"0x69d95138263dc54c07826ef1d76f9eb32ff6bf7fc3fc3281194b46ec7683ceb3","size":450,"type":"ContractTransaction","version":0,"nonce":1237,"sender":"Ad1wDxzcRiRSryvJobNV211Tv7UUiziPXy","valid_until_block":5,"attributes":[],"vin":[],"vout":[],"scripts":[{"invocation":"0c40ae62dadb95b21628921adb9ee14e8c1f20d68b9d6ffba218f8e403af70126449435a53525cd6f1d1c871e64a91025866cd8a413ec70e3cde0ddf62931aa22baa0c40c3e09fc8ad46789d645240634ef40bd180e0dba75beae07a37dc2e899b68b4b3b3a67d076ea9444ed143dbb3264702914a4662ff95859d32c9b5bb5bdc9ec1d60c40b2511115482bd860d85f57103fb8cce2cb45ba8461d4e164685b426cb5df1525e6f09dd7aa23db0fec2241bde7c925e22f3cba3824ea461455515dc2a1455f5e0c40c0aee2528e7fd75a0434f4d67da58507e8d349ca29e01e7eafa9f283e8b8c05d06587db7234f111730c14c8a4bfb7a1ffd9493b9696c71e68966c6bf4a832ab7","verification":"130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b683073b3bb"}]}]}}`, serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"hash":"0x75ab743487ce969792a4cb3c235d9ea5ff97af42b2646183c981437a077fbc45","size":765,"version":0,"nextblockhash":"0xec27fed4ff8ad6a87bdd29aecee43e9ecb4e336321dac875d0b14d2aab1c5798","previousblockhash":"0x6432511ffdb7da7766fac3591efba688793b5368789bdb374de26eaa136bf039","merkleroot":"0xcd9211a3b642d13d1309817b8a575e4ce00e53947912b34aa0ff61f024faf405","time":1588259741,"index":202,"consensus_data":{"primary":0,"nonce":"0000000000000457"},"nextconsensus":"Ad1wDxzcRiRSryvJobNV211Tv7UUiziPXy","confirmations":6,"script":{"invocation":"0c404617b6788538326383015c44ffddd4a05a4e200b65a26fc84234ae8b1e28ef27b7f139dc498c58071193d530ba83081701290eba8f7108397499f5556c16e3780c402131f2bdcc494c73a379e86c46f9e9fe9899a05b23928926b1eaaf816928e160fe971b82263aa1e7efa5f7e46bf99de735fc4fc5aeb81edfdc6a9b2e9fcfa1000c408cbae1582bb9d82de9ff030b8c729737f2157844c0ca29edcdbfed1dd5e2473e0061f0dc29412477417e2c1f7c55443f11b9bd6e0d0856d1ec00240be1b9b9a70c4034d1055531cf0522ac7e5dcd817cf3cd86997ae38da806dc789b1f16eb0005f00b9bc29f7372bb43a8fed040c6763b162c8a0d8e2d0b2d7476e22e0b2b776010","verification":"130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b683073b3bb"},"tx":[{"sys_fee":"0","net_fee":"0","txid":"0x115975f7d1e9c4577ec839c6ea4ae32727616284e2d7cdc5a49e4e421b32e505","size":238,"type":"ContractTransaction","version":0,"nonce":3,"sender":"ASW1VhcukJRrukCXRipY4BE9d9zy4mAYsR","valid_until_block":1200,"attributes":[],"cosigners":[],"vin":[{"txid":"0x1f08a32642a43e3f06b3b9a9355ed274c52de85a886841a2aef7edd94d1dc3f6","vout":0}],"vout":[{"address":"ASW1VhcukJRrukCXRipY4BE9d9zy4mAYsR","asset":"0x7dc12f8835b085d468ddbab3f7152c0e2f5679b5815b2028685abb4608e7b7dc","n":0,"value":"99999000"}],"scripts":[{"invocation":"0c40bfce1ead7d53339440bb29745eed4ad9840875de4f970950065291e7a14cbd249bfbf777d9a997c5e00bbc08e8ce9fdd2cd13c45c3585b4939599ff84c6149ff","verification":"0c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b680a906ad4"}]}]}}`,
result: func(c *Client) interface{} { result: func(c *Client) interface{} {
hash, err := util.Uint256DecodeStringLE("81b74dcfc3a42ef331eec2486231aba300ec9a95b024b0151214c76305d409bb") hash, err := util.Uint256DecodeStringLE("75ab743487ce969792a4cb3c235d9ea5ff97af42b2646183c981437a077fbc45")
if err != nil { if err != nil {
panic(err) panic(err)
} }
nextBlockHash, err := util.Uint256DecodeStringLE("d08e408909ae24de3470a3f231f966e15719910cb7b1a30f356b3e784c1ddf6a") nextBlockHash, err := util.Uint256DecodeStringLE("ec27fed4ff8ad6a87bdd29aecee43e9ecb4e336321dac875d0b14d2aab1c5798")
if err != nil { if err != nil {
panic(err) panic(err)
} }
prevBlockHash, err := util.Uint256DecodeStringLE("a39a1769af16a1aa6715179884d503a1266461cf7100a1c73cb1180956cf255b") prevBlockHash, err := util.Uint256DecodeStringLE("6432511ffdb7da7766fac3591efba688793b5368789bdb374de26eaa136bf039")
if err != nil { if err != nil {
panic(err) panic(err)
} }
merkleRoot, err := util.Uint256DecodeStringLE("e74f202d6a9a8dd6ed52f7a8e1bbde8b0f86f801cad5692d391643de9a355cc0") merkleRoot, err := util.Uint256DecodeStringLE("cd9211a3b642d13d1309817b8a575e4ce00e53947912b34aa0ff61f024faf405")
if err != nil { if err != nil {
panic(err) panic(err)
} }
invScript, err := hex.DecodeString("0c407f70912fdcd0ce9a70963110eb39ef73ed02512ea63bc1c55577474df8f364a874999b16666888f8a3ca69873054a31ab42dd1fee1ed8605ff65edb39797e34e0c40d59459cb1cc8270d9a813aa444852a5d7714aed0b25373ec07549349c6db32b6c8c2e43cdd7b1ad2a4154cb5ff1222d9d7010877016a59775abdd2c08f253c850c4048b65cb58e0d58f2dd0f5db3b195c5784933d6179d8317217ba46f9deac9a667bffb1afd0dd86bdae6267af4c2605f7401849a0b980fcecfeae95df7f5ca6e140c406d9b1942c5aa80070ad484b3dd76421a3f46327670bea94e3ecb3a94eef8843a75195b55872a7c503acb01ab368cdaa11a9e5fb80d55bbd6b861233ba84edcf8") invScript, err := hex.DecodeString("0c404617b6788538326383015c44ffddd4a05a4e200b65a26fc84234ae8b1e28ef27b7f139dc498c58071193d530ba83081701290eba8f7108397499f5556c16e3780c402131f2bdcc494c73a379e86c46f9e9fe9899a05b23928926b1eaaf816928e160fe971b82263aa1e7efa5f7e46bf99de735fc4fc5aeb81edfdc6a9b2e9fcfa1000c408cbae1582bb9d82de9ff030b8c729737f2157844c0ca29edcdbfed1dd5e2473e0061f0dc29412477417e2c1f7c55443f11b9bd6e0d0856d1ec00240be1b9b9a70c4034d1055531cf0522ac7e5dcd817cf3cd86997ae38da806dc789b1f16eb0005f00b9bc29f7372bb43a8fed040c6763b162c8a0d8e2d0b2d7476e22e0b2b776010")
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -290,21 +313,29 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
if err != nil { if err != nil {
panic(err) panic(err)
} }
sender, err := address.StringToUint160("Ad1wDxzcRiRSryvJobNV211Tv7UUiziPXy") sender, err := address.StringToUint160("ASW1VhcukJRrukCXRipY4BE9d9zy4mAYsR")
if err != nil { if err != nil {
panic(err) panic(err)
} }
txInvScript, err := hex.DecodeString("0c40ae62dadb95b21628921adb9ee14e8c1f20d68b9d6ffba218f8e403af70126449435a53525cd6f1d1c871e64a91025866cd8a413ec70e3cde0ddf62931aa22baa0c40c3e09fc8ad46789d645240634ef40bd180e0dba75beae07a37dc2e899b68b4b3b3a67d076ea9444ed143dbb3264702914a4662ff95859d32c9b5bb5bdc9ec1d60c40b2511115482bd860d85f57103fb8cce2cb45ba8461d4e164685b426cb5df1525e6f09dd7aa23db0fec2241bde7c925e22f3cba3824ea461455515dc2a1455f5e0c40c0aee2528e7fd75a0434f4d67da58507e8d349ca29e01e7eafa9f283e8b8c05d06587db7234f111730c14c8a4bfb7a1ffd9493b9696c71e68966c6bf4a832ab7") txInvScript, err := hex.DecodeString("0c40bfce1ead7d53339440bb29745eed4ad9840875de4f970950065291e7a14cbd249bfbf777d9a997c5e00bbc08e8ce9fdd2cd13c45c3585b4939599ff84c6149ff")
if err != nil { if err != nil {
panic(err) panic(err)
} }
txVerifScript, err := hex.DecodeString("130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b683073b3bb") txVerifScript, err := hex.DecodeString("0c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b680a906ad4")
if err != nil {
panic(err)
}
vin, err := util.Uint256DecodeStringLE("1f08a32642a43e3f06b3b9a9355ed274c52de85a886841a2aef7edd94d1dc3f6")
if err != nil {
panic(err)
}
outAddress, err := address.StringToUint160("ASW1VhcukJRrukCXRipY4BE9d9zy4mAYsR")
if err != nil { if err != nil {
panic(err) panic(err)
} }
tx := transaction.NewContractTX() tx := transaction.NewContractTX()
tx.Nonce = 1237 tx.Nonce = 3
tx.ValidUntilBlock = 5 tx.ValidUntilBlock = 1200
tx.Sender = sender tx.Sender = sender
tx.Scripts = []transaction.Witness{ tx.Scripts = []transaction.Witness{
{ {
@ -312,19 +343,34 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
VerificationScript: txVerifScript, VerificationScript: txVerifScript,
}, },
} }
tx.Inputs = []transaction.Input{
{
PrevHash: vin,
PrevIndex: 0,
},
}
tx.Outputs = []transaction.Output{
{
AssetID: core.GoverningTokenID(),
Amount: util.Fixed8FromInt64(99999000),
ScriptHash: outAddress,
Position: 0,
},
}
// Update hashes for correct result comparison. // Update hashes for correct result comparison.
_ = tx.Hash() _ = tx.Hash()
return &result.Block{ return &result.Block{
Hash: hash, Hash: hash,
Size: 977, Size: 765,
Version: 0, Version: 0,
NextBlockHash: &nextBlockHash, NextBlockHash: &nextBlockHash,
PreviousBlockHash: prevBlockHash, PreviousBlockHash: prevBlockHash,
MerkleRoot: merkleRoot, MerkleRoot: merkleRoot,
Time: 1587727442, Time: 1588259741,
Index: 5, Index: 202,
NextConsensus: "Ad1wDxzcRiRSryvJobNV211Tv7UUiziPXy", NextConsensus: "Ad1wDxzcRiRSryvJobNV211Tv7UUiziPXy",
Confirmations: 203, Confirmations: 6,
ConsensusData: result.ConsensusData{ ConsensusData: result.ConsensusData{
PrimaryIndex: 0, PrimaryIndex: 0,
Nonce: "0000000000000457", Nonce: "0000000000000457",
@ -653,19 +699,19 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
{ {
name: "positive", name: "positive",
invoke: func(c *Client) (i interface{}, err error) { invoke: func(c *Client) (i interface{}, err error) {
hash, err := util.Uint256DecodeStringLE("b3ce8376ec464b198132fcc37fbff62fb39e6fd7f16e82074cc53d263851d969") hash, err := util.Uint256DecodeStringLE("05e5321b424e9ea4c5cdd7e28462612727e34aeac639c87e57c4e9d1f7755911")
if err != nil { if err != nil {
panic(err) panic(err)
} }
return c.GetRawTransaction(hash) return c.GetRawTransaction(hash)
}, },
serverResponse: `{"id":1,"jsonrpc":"2.0","result":"8000d5040000e903736ceceeceae1806eee0e3ec61e7cce476ce0500000000000001fd08010c40ae62dadb95b21628921adb9ee14e8c1f20d68b9d6ffba218f8e403af70126449435a53525cd6f1d1c871e64a91025866cd8a413ec70e3cde0ddf62931aa22baa0c40c3e09fc8ad46789d645240634ef40bd180e0dba75beae07a37dc2e899b68b4b3b3a67d076ea9444ed143dbb3264702914a4662ff95859d32c9b5bb5bdc9ec1d60c40b2511115482bd860d85f57103fb8cce2cb45ba8461d4e164685b426cb5df1525e6f09dd7aa23db0fec2241bde7c925e22f3cba3824ea461455515dc2a1455f5e0c40c0aee2528e7fd75a0434f4d67da58507e8d349ca29e01e7eafa9f283e8b8c05d06587db7234f111730c14c8a4bfb7a1ffd9493b9696c71e68966c6bf4a832ab794130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b683073b3bb"}`, serverResponse: `{"id":1,"jsonrpc":"2.0","result":"80000300000075a94799633ed955dd85a8af314a5b435ab51903b0040000000001f6c31d4dd9edf7aea24168885ae82dc574d25e35a9b9b3063f3ea44226a3081f000001dcb7e70846bb5a6828205b81b579562f0e2c15f7b3badd68d485b035882fc17d00184a27db86230075a94799633ed955dd85a8af314a5b435ab5190301420c40bfce1ead7d53339440bb29745eed4ad9840875de4f970950065291e7a14cbd249bfbf777d9a997c5e00bbc08e8ce9fdd2cd13c45c3585b4939599ff84c6149ff290c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b680a906ad4"}`,
result: func(c *Client) interface{} { return &transaction.Transaction{} }, result: func(c *Client) interface{} { return &transaction.Transaction{} },
check: func(t *testing.T, c *Client, result interface{}) { check: func(t *testing.T, c *Client, result interface{}) {
res, ok := result.(*transaction.Transaction) res, ok := result.(*transaction.Transaction)
require.True(t, ok) require.True(t, ok)
assert.Equal(t, uint8(0), res.Version) assert.Equal(t, uint8(0), res.Version)
assert.Equal(t, "b3ce8376ec464b198132fcc37fbff62fb39e6fd7f16e82074cc53d263851d969", res.Hash().StringBE()) assert.Equal(t, "05e5321b424e9ea4c5cdd7e28462612727e34aeac639c87e57c4e9d1f7755911", res.Hash().StringBE())
assert.Equal(t, transaction.ContractType, res.Type) assert.Equal(t, transaction.ContractType, res.Type)
assert.Equal(t, false, res.Trimmed) assert.Equal(t, false, res.Trimmed)
}, },
@ -673,33 +719,41 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
{ {
name: "verbose_positive", name: "verbose_positive",
invoke: func(c *Client) (interface{}, error) { invoke: func(c *Client) (interface{}, error) {
hash, err := util.Uint256DecodeStringLE("b3ce8376ec464b198132fcc37fbff62fb39e6fd7f16e82074cc53d263851d969") hash, err := util.Uint256DecodeStringLE("05e5321b424e9ea4c5cdd7e28462612727e34aeac639c87e57c4e9d1f7755911")
if err != nil { if err != nil {
panic(err) panic(err)
} }
return c.GetRawTransactionVerbose(hash) return c.GetRawTransactionVerbose(hash)
}, },
serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"sys_fee":"0","net_fee":"0","blockhash":"0x81b74dcfc3a42ef331eec2486231aba300ec9a95b024b0151214c76305d409bb","confirmations":205,"blocktime":1587727442,"txid":"0x69d95138263dc54c07826ef1d76f9eb32ff6bf7fc3fc3281194b46ec7683ceb3","size":450,"type":"ContractTransaction","version":0,"nonce":1237,"sender":"Ad1wDxzcRiRSryvJobNV211Tv7UUiziPXy","valid_until_block":5,"attributes":[],"vin":[],"vout":[],"scripts":[{"invocation":"0c40ae62dadb95b21628921adb9ee14e8c1f20d68b9d6ffba218f8e403af70126449435a53525cd6f1d1c871e64a91025866cd8a413ec70e3cde0ddf62931aa22baa0c40c3e09fc8ad46789d645240634ef40bd180e0dba75beae07a37dc2e899b68b4b3b3a67d076ea9444ed143dbb3264702914a4662ff95859d32c9b5bb5bdc9ec1d60c40b2511115482bd860d85f57103fb8cce2cb45ba8461d4e164685b426cb5df1525e6f09dd7aa23db0fec2241bde7c925e22f3cba3824ea461455515dc2a1455f5e0c40c0aee2528e7fd75a0434f4d67da58507e8d349ca29e01e7eafa9f283e8b8c05d06587db7234f111730c14c8a4bfb7a1ffd9493b9696c71e68966c6bf4a832ab7","verification":"130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b683073b3bb"}]}}`, serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"sys_fee":"0","net_fee":"0","blockhash":"0x75ab743487ce969792a4cb3c235d9ea5ff97af42b2646183c981437a077fbc45","confirmations":8,"blocktime":1588259741,"txid":"0x115975f7d1e9c4577ec839c6ea4ae32727616284e2d7cdc5a49e4e421b32e505","size":238,"type":"ContractTransaction","version":0,"nonce":3,"sender":"ASW1VhcukJRrukCXRipY4BE9d9zy4mAYsR","valid_until_block":1200,"attributes":[],"cosigners":[],"vin":[{"txid":"0x1f08a32642a43e3f06b3b9a9355ed274c52de85a886841a2aef7edd94d1dc3f6","vout":0}],"vout":[{"address":"ASW1VhcukJRrukCXRipY4BE9d9zy4mAYsR","asset":"0x7dc12f8835b085d468ddbab3f7152c0e2f5679b5815b2028685abb4608e7b7dc","n":0,"value":"99999000"}],"scripts":[{"invocation":"0c40bfce1ead7d53339440bb29745eed4ad9840875de4f970950065291e7a14cbd249bfbf777d9a997c5e00bbc08e8ce9fdd2cd13c45c3585b4939599ff84c6149ff","verification":"0c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b680a906ad4"}]}}`,
result: func(c *Client) interface{} { result: func(c *Client) interface{} {
blockHash, err := util.Uint256DecodeStringLE("81b74dcfc3a42ef331eec2486231aba300ec9a95b024b0151214c76305d409bb") blockHash, err := util.Uint256DecodeStringLE("75ab743487ce969792a4cb3c235d9ea5ff97af42b2646183c981437a077fbc45")
if err != nil { if err != nil {
panic(err) panic(err)
} }
sender, err := address.StringToUint160("Ad1wDxzcRiRSryvJobNV211Tv7UUiziPXy") sender, err := address.StringToUint160("ASW1VhcukJRrukCXRipY4BE9d9zy4mAYsR")
if err != nil { if err != nil {
panic(err) panic(err)
} }
invocation, err := hex.DecodeString("0c40ae62dadb95b21628921adb9ee14e8c1f20d68b9d6ffba218f8e403af70126449435a53525cd6f1d1c871e64a91025866cd8a413ec70e3cde0ddf62931aa22baa0c40c3e09fc8ad46789d645240634ef40bd180e0dba75beae07a37dc2e899b68b4b3b3a67d076ea9444ed143dbb3264702914a4662ff95859d32c9b5bb5bdc9ec1d60c40b2511115482bd860d85f57103fb8cce2cb45ba8461d4e164685b426cb5df1525e6f09dd7aa23db0fec2241bde7c925e22f3cba3824ea461455515dc2a1455f5e0c40c0aee2528e7fd75a0434f4d67da58507e8d349ca29e01e7eafa9f283e8b8c05d06587db7234f111730c14c8a4bfb7a1ffd9493b9696c71e68966c6bf4a832ab7") invocation, err := hex.DecodeString("0c40bfce1ead7d53339440bb29745eed4ad9840875de4f970950065291e7a14cbd249bfbf777d9a997c5e00bbc08e8ce9fdd2cd13c45c3585b4939599ff84c6149ff")
if err != nil { if err != nil {
panic(err) panic(err)
} }
verification, err := hex.DecodeString("130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b683073b3bb") verification, err := hex.DecodeString("0c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b680a906ad4")
if err != nil {
panic(err)
}
vin, err := util.Uint256DecodeStringLE("1f08a32642a43e3f06b3b9a9355ed274c52de85a886841a2aef7edd94d1dc3f6")
if err != nil {
panic(err)
}
outAddress, err := address.StringToUint160("ASW1VhcukJRrukCXRipY4BE9d9zy4mAYsR")
if err != nil { if err != nil {
panic(err) panic(err)
} }
tx := transaction.NewContractTX() tx := transaction.NewContractTX()
tx.Nonce = 1237 tx.Nonce = 3
tx.ValidUntilBlock = 5 tx.ValidUntilBlock = 1200
tx.Sender = sender tx.Sender = sender
tx.Scripts = []transaction.Witness{ tx.Scripts = []transaction.Witness{
{ {
@ -707,6 +761,21 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
VerificationScript: verification, VerificationScript: verification,
}, },
} }
tx.Inputs = []transaction.Input{
{
PrevHash: vin,
PrevIndex: 0,
},
}
tx.Outputs = []transaction.Output{
{
AssetID: core.GoverningTokenID(),
Amount: util.Fixed8FromInt64(99999000),
ScriptHash: outAddress,
Position: 0,
},
}
// Update hashes for correct result comparison. // Update hashes for correct result comparison.
_ = tx.Hash() _ = tx.Hash()
@ -716,8 +785,8 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
SysFee: 0, SysFee: 0,
NetFee: 0, NetFee: 0,
Blockhash: blockHash, Blockhash: blockHash,
Confirmations: 205, Confirmations: 8,
Timestamp: uint64(1587727442), Timestamp: uint64(1588259741),
}, },
} }
}, },

View file

@ -53,12 +53,12 @@ var rpcTestCases = map[string][]rpcTestCase{
"getapplicationlog": { "getapplicationlog": {
{ {
name: "positive", name: "positive",
params: `["953b6e5ba85dd068c2eba5afd2ffdf8ff832b3a791976e2fd896baf5737b2616"]`, params: `["9872fd330d63d64beba7122fcc9678e7f380d2163acff6a7d4f67a3c01cd67d1"]`,
result: func(e *executor) interface{} { return &result.ApplicationLog{} }, result: func(e *executor) interface{} { return &result.ApplicationLog{} },
check: func(t *testing.T, e *executor, acc interface{}) { check: func(t *testing.T, e *executor, acc interface{}) {
res, ok := acc.(*result.ApplicationLog) res, ok := acc.(*result.ApplicationLog)
require.True(t, ok) require.True(t, ok)
expectedTxHash, err := util.Uint256DecodeStringLE("953b6e5ba85dd068c2eba5afd2ffdf8ff832b3a791976e2fd896baf5737b2616") expectedTxHash, err := util.Uint256DecodeStringLE("9872fd330d63d64beba7122fcc9678e7f380d2163acff6a7d4f67a3c01cd67d1")
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, expectedTxHash, res.TxHash) assert.Equal(t, expectedTxHash, res.TxHash)
assert.Equal(t, 1, len(res.Executions)) assert.Equal(t, 1, len(res.Executions))
@ -253,7 +253,7 @@ var rpcTestCases = map[string][]rpcTestCase{
"getassetstate": { "getassetstate": {
{ {
name: "positive", name: "positive",
params: `["b16384a950ed01ed5fc15c03fe7b98228871cb43b1bc22d67029449fc854d104"]`, params: `["8ef63ccd6f4ea20a93e7f4e84b2d43f778077612b241d617e42e1750cca4f2c5"]`,
result: func(e *executor) interface{} { return &result.AssetState{} }, result: func(e *executor) interface{} { return &result.AssetState{} },
check: func(t *testing.T, e *executor, as interface{}) { check: func(t *testing.T, e *executor, as interface{}) {
res, ok := as.(*result.AssetState) res, ok := as.(*result.AssetState)
@ -472,7 +472,7 @@ var rpcTestCases = map[string][]rpcTestCase{
params: `["` + testchain.MultisigAddress() + `"]`, params: `["` + testchain.MultisigAddress() + `"]`,
result: func(*executor) interface{} { result: func(*executor) interface{} {
// hash of the issueTx // hash of the issueTx
h, _ := util.Uint256DecodeStringBE("8a4711012932f4f79f9534803feab0ef85e7a313c52a36f5d56b9f8ec190bd92") h, _ := util.Uint256DecodeStringBE("99bd2bb2791887ddd3f64dac70ac15339956c76e7c306a1202372ff24fe30635")
amount := util.Fixed8FromInt64(1 * 8) // (endHeight - startHeight) * genAmount[0] amount := util.Fixed8FromInt64(1 * 8) // (endHeight - startHeight) * genAmount[0]
return &result.ClaimableInfo{ return &result.ClaimableInfo{
Spents: []result.Claimable{ Spents: []result.Claimable{
@ -531,7 +531,7 @@ var rpcTestCases = map[string][]rpcTestCase{
"gettransactionheight": { "gettransactionheight": {
{ {
name: "positive", name: "positive",
params: `["5f1e841f625d52dd3d73bbf5203f8468835353b7c476a4d367161ea959944981"]`, params: `["1f08a32642a43e3f06b3b9a9355ed274c52de85a886841a2aef7edd94d1dc3f6"]`,
result: func(e *executor) interface{} { result: func(e *executor) interface{} {
h := 1 h := 1
return &h return &h
@ -743,7 +743,7 @@ var rpcTestCases = map[string][]rpcTestCase{
"sendrawtransaction": { "sendrawtransaction": {
{ {
name: "positive", name: "positive",
params: `["80000b00000075a94799633ed955dd85a8af314a5b435ab51903b004000000011e4db58df4326140a371d0b0cabecea70226b93157dfb561c73ba8db599ebcb6010001f5bc5a9ac7b85a47be381260a06b5a1e7a667ce8f7d7c8baa5cfc6465571377a0030d3dec386230075a94799633ed955dd85a8af314a5b435ab5190301420c401b3040b6eea83bfbd555554c94e7a0e6077922769f3ac19c1183e14dfd1d6ef6a87658b5499921ac59ae2d2acac10d8f0f6147620e27616bb5b7305fb36b6ce0290c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b680a906ad4"]`, params: `["80000b00000075a94799633ed955dd85a8af314a5b435ab51903b0040000000001e53e6c239e3d8441f623ea7b48cdea60c6ae0426a8bac04296002babfeafe5a4010001dcb7e70846bb5a6828205b81b579562f0e2c15f7b3badd68d485b035882fc17d0030d3dec386230075a94799633ed955dd85a8af314a5b435ab5190301420c408378eb6bdba1f14540cf2920a4d49c52327f617dc861a21d1c085346aaae2abd410e28dc6a03471a78e71246696a591b9677a71144dfba557d1c781c8c97e350290c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20b680a906ad4"]`,
result: func(e *executor) interface{} { result: func(e *executor) interface{} {
v := true v := true
return &v return &v
@ -890,7 +890,7 @@ func TestRPC(t *testing.T) {
var res string var res string
err := json.Unmarshal(result, &res) err := json.Unmarshal(result, &res)
require.NoErrorf(t, err, "could not parse response: %s", result) require.NoErrorf(t, err, "could not parse response: %s", result)
assert.Equal(t, "400000000000da1745e9b549bd0bfa1a569971c77eba30cd5a4b0000000000455b7b226c616e67223a227a682d434e222c226e616d65223a22e5b08fe89a81e882a1227d2c7b226c616e67223a22656e222c226e616d65223a22416e745368617265227d5d0000c16ff28623000000da1745e9b549bd0bfa1a569971c77eba30cd5a4b00000000", res) assert.Equal(t, "400000000000da1745e9b549bd0bfa1a569971c77eba30cd5a4b0000000000455b7b226c616e67223a227a682d434e222c226e616d65223a22e5b08fe89a81e882a1227d2c7b226c616e67223a22656e222c226e616d65223a22416e745368617265227d5d0000c16ff28623000000da1745e9b549bd0bfa1a569971c77eba30cd5a4b0000000000", res)
}) })
t.Run("getrawtransaction 2 arguments", func(t *testing.T) { t.Run("getrawtransaction 2 arguments", func(t *testing.T) {
@ -902,7 +902,7 @@ func TestRPC(t *testing.T) {
var res string var res string
err := json.Unmarshal(result, &res) err := json.Unmarshal(result, &res)
require.NoErrorf(t, err, "could not parse response: %s", result) require.NoErrorf(t, err, "could not parse response: %s", result)
assert.Equal(t, "400000000000da1745e9b549bd0bfa1a569971c77eba30cd5a4b0000000000455b7b226c616e67223a227a682d434e222c226e616d65223a22e5b08fe89a81e882a1227d2c7b226c616e67223a22656e222c226e616d65223a22416e745368617265227d5d0000c16ff28623000000da1745e9b549bd0bfa1a569971c77eba30cd5a4b00000000", res) assert.Equal(t, "400000000000da1745e9b549bd0bfa1a569971c77eba30cd5a4b0000000000455b7b226c616e67223a227a682d434e222c226e616d65223a22e5b08fe89a81e882a1227d2c7b226c616e67223a22656e222c226e616d65223a22416e745368617265227d5d0000c16ff28623000000da1745e9b549bd0bfa1a569971c77eba30cd5a4b0000000000", res)
}) })
t.Run("getrawtransaction 2 arguments, verbose", func(t *testing.T) { t.Run("getrawtransaction 2 arguments, verbose", func(t *testing.T) {
@ -991,7 +991,7 @@ func TestRPC(t *testing.T) {
err := json.Unmarshal(res, &txOut) err := json.Unmarshal(res, &txOut)
require.NoErrorf(t, err, "could not parse response: %s", res) require.NoErrorf(t, err, "could not parse response: %s", res)
assert.Equal(t, 0, txOut.N) assert.Equal(t, 0, txOut.N)
assert.Equal(t, "0xf5bc5a9ac7b85a47be381260a06b5a1e7a667ce8f7d7c8baa5cfc6465571377a", txOut.Asset) assert.Equal(t, "0xdcb7e70846bb5a6828205b81b579562f0e2c15f7b3badd68d485b035882fc17d", txOut.Asset)
assert.Equal(t, util.Fixed8FromInt64(100000000), txOut.Value) assert.Equal(t, util.Fixed8FromInt64(100000000), txOut.Value)
assert.Equal(t, testchain.MultisigAddress(), txOut.Address) assert.Equal(t, testchain.MultisigAddress(), txOut.Address)
}) })

Binary file not shown.

View file

@ -5,6 +5,8 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"strings" "strings"
"github.com/nspcc-dev/neo-go/pkg/io"
) )
// Uint160Size is the size of Uint160 in bytes. // Uint160Size is the size of Uint160 in bytes.
@ -131,3 +133,13 @@ func (u *Uint160) UnmarshalJSON(data []byte) (err error) {
func (u Uint160) MarshalJSON() ([]byte, error) { func (u Uint160) MarshalJSON() ([]byte, error) {
return []byte(`"0x` + u.StringLE() + `"`), nil return []byte(`"0x` + u.StringLE() + `"`), nil
} }
// EncodeBinary implements Serializable interface.
func (u *Uint160) EncodeBinary(bw *io.BinWriter) {
bw.WriteBytes(u[:])
}
// DecodeBinary implements Serializable interface.
func (u *Uint160) DecodeBinary(br *io.BinReader) {
br.ReadBytes(u[:])
}

View file

@ -223,10 +223,14 @@ func (c *Context) String() string {
return "execution context" return "execution context"
} }
// GetContextScriptHash returns script hash of the invocation stack element // getContextScriptHash returns script hash of the invocation stack element
// number n. // number n.
func (v *VM) GetContextScriptHash(n int) util.Uint160 { func (v *VM) getContextScriptHash(n int) util.Uint160 {
ctxIface := v.Istack().Peek(n).Value() element := v.Istack().Peek(n)
if element == nil {
return util.Uint160{}
}
ctxIface := element.Value()
ctx := ctxIface.(*Context) ctx := ctxIface.(*Context)
return ctx.ScriptHash() return ctx.ScriptHash()
} }
@ -234,7 +238,7 @@ func (v *VM) GetContextScriptHash(n int) util.Uint160 {
// PushContextScriptHash pushes to evaluation stack the script hash of the // PushContextScriptHash pushes to evaluation stack the script hash of the
// invocation stack element number n. // invocation stack element number n.
func (v *VM) PushContextScriptHash(n int) error { func (v *VM) PushContextScriptHash(n int) error {
h := v.GetContextScriptHash(n) h := v.getContextScriptHash(n)
v.Estack().PushVal(h.BytesBE()) v.Estack().PushVal(h.BytesBE())
return nil return nil
} }

View file

@ -34,6 +34,13 @@ func newError(ip int, op opcode.Opcode, err interface{}) *errorAtInstruct {
// StateMessage is a vm state message which could be used as additional info for example by cli. // StateMessage is a vm state message which could be used as additional info for example by cli.
type StateMessage string type StateMessage string
// ScriptHashGetter defines an interface for getting calling, entry and current script hashes.
type ScriptHashGetter interface {
GetCallingScriptHash() util.Uint160
GetEntryScriptHash() util.Uint160
GetCurrentScriptHash() util.Uint160
}
const ( const (
// MaxArraySize is the maximum array size allowed in the VM. // MaxArraySize is the maximum array size allowed in the VM.
MaxArraySize = 1024 MaxArraySize = 1024
@ -1527,3 +1534,18 @@ func (v *VM) bytesToPublicKey(b []byte) *keys.PublicKey {
} }
return pkey return pkey
} }
// GetCallingScriptHash implements ScriptHashGetter interface
func (v *VM) GetCallingScriptHash() util.Uint160 {
return v.getContextScriptHash(1)
}
// GetEntryScriptHash implements ScriptHashGetter interface
func (v *VM) GetEntryScriptHash() util.Uint160 {
return v.getContextScriptHash(v.Istack().Len() - 1)
}
// GetCurrentScriptHash implements ScriptHashGetter interface
func (v *VM) GetCurrentScriptHash() util.Uint160 {
return v.getContextScriptHash(0)
}