mirror of https://github.com/poanetwork/gecko.git
180 lines
4.7 KiB
Go
180 lines
4.7 KiB
Go
|
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
||
|
// See the file LICENSE for licensing terms.
|
||
|
|
||
|
package secp256k1fx
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
|
||
|
"github.com/ava-labs/gecko/utils/crypto"
|
||
|
"github.com/ava-labs/gecko/utils/hashing"
|
||
|
"github.com/ava-labs/gecko/vms/components/verify"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
errWrongVMType = errors.New("wrong vm type")
|
||
|
errWrongTxType = errors.New("wrong tx type")
|
||
|
errWrongUTXOType = errors.New("wrong utxo type")
|
||
|
errWrongOutputType = errors.New("wrong output type")
|
||
|
errWrongInputType = errors.New("wrong input type")
|
||
|
errWrongCredentialType = errors.New("wrong credential type")
|
||
|
|
||
|
errWrongNumberOfOutputs = errors.New("wrong number of outputs for an operation")
|
||
|
errWrongNumberOfInputs = errors.New("wrong number of inputs for an operation")
|
||
|
errWrongNumberOfCredentials = errors.New("wrong number of credentials for an operation")
|
||
|
|
||
|
errWrongMintCreated = errors.New("wrong mint output created from the operation")
|
||
|
|
||
|
errWrongAmounts = errors.New("input is consuming a different amount than expected")
|
||
|
errTimelocked = errors.New("output is time locked")
|
||
|
errTooManySigners = errors.New("input has more signers than expected")
|
||
|
errTooFewSigners = errors.New("input has less signers than expected")
|
||
|
errInputCredentialSignersMismatch = errors.New("input expected a different number of signers than provided in the credential")
|
||
|
errWrongSigner = errors.New("credential does not produce expected signer")
|
||
|
)
|
||
|
|
||
|
// Fx ...
|
||
|
type Fx struct {
|
||
|
vm VM
|
||
|
secpFactory crypto.FactorySECP256K1R
|
||
|
}
|
||
|
|
||
|
// Initialize ...
|
||
|
func (fx *Fx) Initialize(vmIntf interface{}) error {
|
||
|
vm, ok := vmIntf.(VM)
|
||
|
if !ok {
|
||
|
return errWrongVMType
|
||
|
}
|
||
|
|
||
|
c := vm.Codec()
|
||
|
c.RegisterType(&MintOutput{})
|
||
|
c.RegisterType(&TransferOutput{})
|
||
|
c.RegisterType(&MintInput{})
|
||
|
c.RegisterType(&TransferInput{})
|
||
|
c.RegisterType(&Credential{})
|
||
|
|
||
|
fx.vm = vm
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// VerifyOperation ...
|
||
|
func (fx *Fx) VerifyOperation(txIntf interface{}, utxosIntf, insIntf, credsIntf, outsIntf []interface{}) error {
|
||
|
tx, ok := txIntf.(Tx)
|
||
|
if !ok {
|
||
|
return errWrongTxType
|
||
|
}
|
||
|
|
||
|
if len(outsIntf) != 2 {
|
||
|
return errWrongNumberOfOutputs
|
||
|
}
|
||
|
if len(utxosIntf) != 1 || len(insIntf) != 1 {
|
||
|
return errWrongNumberOfInputs
|
||
|
}
|
||
|
if len(credsIntf) != 1 {
|
||
|
return errWrongNumberOfCredentials
|
||
|
}
|
||
|
|
||
|
utxo, ok := utxosIntf[0].(*MintOutput)
|
||
|
if !ok {
|
||
|
return errWrongUTXOType
|
||
|
}
|
||
|
in, ok := insIntf[0].(*MintInput)
|
||
|
if !ok {
|
||
|
return errWrongInputType
|
||
|
}
|
||
|
cred, ok := credsIntf[0].(*Credential)
|
||
|
if !ok {
|
||
|
return errWrongCredentialType
|
||
|
}
|
||
|
newMint, ok := outsIntf[0].(*MintOutput)
|
||
|
if !ok {
|
||
|
return errWrongOutputType
|
||
|
}
|
||
|
newOutput, ok := outsIntf[1].(*TransferOutput)
|
||
|
if !ok {
|
||
|
return errWrongOutputType
|
||
|
}
|
||
|
|
||
|
return fx.verifyOperation(tx, utxo, in, cred, newMint, newOutput)
|
||
|
}
|
||
|
|
||
|
func (fx *Fx) verifyOperation(tx Tx, utxo *MintOutput, in *MintInput, cred *Credential, newMint *MintOutput, newOutput *TransferOutput) error {
|
||
|
if err := verify.All(utxo, in, cred, newMint, newOutput); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
if !utxo.Equals(&newMint.OutputOwners) {
|
||
|
return errWrongMintCreated
|
||
|
}
|
||
|
|
||
|
return fx.verifyCredentials(tx, &utxo.OutputOwners, &in.Input, cred)
|
||
|
}
|
||
|
|
||
|
// VerifyTransfer ...
|
||
|
func (fx *Fx) VerifyTransfer(txIntf, utxoIntf, inIntf, credIntf interface{}) error {
|
||
|
tx, ok := txIntf.(Tx)
|
||
|
if !ok {
|
||
|
return errWrongTxType
|
||
|
}
|
||
|
utxo, ok := utxoIntf.(*TransferOutput)
|
||
|
if !ok {
|
||
|
return errWrongUTXOType
|
||
|
}
|
||
|
in, ok := inIntf.(*TransferInput)
|
||
|
if !ok {
|
||
|
return errWrongInputType
|
||
|
}
|
||
|
cred, ok := credIntf.(*Credential)
|
||
|
if !ok {
|
||
|
return errWrongCredentialType
|
||
|
}
|
||
|
return fx.verifyTransfer(tx, utxo, in, cred)
|
||
|
}
|
||
|
|
||
|
func (fx *Fx) verifyTransfer(tx Tx, utxo *TransferOutput, in *TransferInput, cred *Credential) error {
|
||
|
if err := verify.All(utxo, in, cred); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
clock := fx.vm.Clock()
|
||
|
switch {
|
||
|
case utxo.Amt != in.Amt:
|
||
|
return errWrongAmounts
|
||
|
case utxo.Locktime > clock.Unix():
|
||
|
return errTimelocked
|
||
|
}
|
||
|
|
||
|
return fx.verifyCredentials(tx, &utxo.OutputOwners, &in.Input, cred)
|
||
|
}
|
||
|
|
||
|
func (fx *Fx) verifyCredentials(tx Tx, out *OutputOwners, in *Input, cred *Credential) error {
|
||
|
numSigs := len(in.SigIndices)
|
||
|
switch {
|
||
|
case out.Threshold < uint32(numSigs):
|
||
|
return errTooManySigners
|
||
|
case out.Threshold > uint32(numSigs):
|
||
|
return errTooFewSigners
|
||
|
case numSigs != len(cred.Sigs):
|
||
|
return errInputCredentialSignersMismatch
|
||
|
}
|
||
|
|
||
|
txBytes := tx.UnsignedBytes()
|
||
|
txHash := hashing.ComputeHash256(txBytes)
|
||
|
|
||
|
for i, index := range in.SigIndices {
|
||
|
sig := cred.Sigs[i]
|
||
|
|
||
|
pk, err := fx.secpFactory.RecoverHashPublicKey(txHash, sig[:])
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
expectedAddress := out.Addrs[index]
|
||
|
if !expectedAddress.Equals(pk.Address()) {
|
||
|
return errWrongSigner
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|