forked from TrueCloudLab/frostfs-contract
[#51] balance: Support notary contract
Signed-off-by: Alex Vanin <alexey@nspcc.ru>
This commit is contained in:
parent
299c888266
commit
c6816193d3
1 changed files with 64 additions and 160 deletions
|
@ -113,31 +113,11 @@ func Transfer(from, to interop.Hash160, amount int, data interface{}) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TransferX(from, to interop.Hash160, amount int, details []byte) bool {
|
func TransferX(from, to interop.Hash160, amount int, details []byte) bool {
|
||||||
var (
|
multiaddr := common.InnerRingMultiAddressViaStorage(ctx, netmapContractKey)
|
||||||
n int // number of votes for inner ring invoke
|
if !runtime.CheckWitness(multiaddr) {
|
||||||
hashTxID []byte // ballot key of the inner ring invocation
|
|
||||||
)
|
|
||||||
|
|
||||||
innerRing := irList()
|
|
||||||
threshold := len(innerRing)/3*2 + 1
|
|
||||||
|
|
||||||
irKey := common.InnerRingInvoker(innerRing)
|
|
||||||
if len(irKey) == 0 {
|
|
||||||
panic("transferX: this method must be invoked from inner ring")
|
panic("transferX: this method must be invoked from inner ring")
|
||||||
}
|
}
|
||||||
|
|
||||||
fromKnownContract := fromKnownContract(runtime.GetCallingScriptHash())
|
|
||||||
if fromKnownContract {
|
|
||||||
n = threshold
|
|
||||||
runtime.Log("transferX: processed indirect invoke")
|
|
||||||
} else {
|
|
||||||
hashTxID = common.InvokeID([]interface{}{from, to, amount}, []byte("transfer"))
|
|
||||||
n = common.Vote(ctx, hashTxID, irKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
if n >= threshold {
|
|
||||||
common.RemoveVotes(ctx, hashTxID)
|
|
||||||
|
|
||||||
result := token.transfer(ctx, from, to, amount, true, details)
|
result := token.transfer(ctx, from, to, amount, true, details)
|
||||||
if result {
|
if result {
|
||||||
runtime.Log("transferX: success")
|
runtime.Log("transferX: success")
|
||||||
|
@ -149,24 +129,12 @@ func TransferX(from, to interop.Hash160, amount int, details []byte) bool {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func Lock(txID []byte, from, to interop.Hash160, amount, until int) bool {
|
func Lock(txID []byte, from, to interop.Hash160, amount, until int) bool {
|
||||||
innerRing := irList()
|
multiaddr := common.InnerRingMultiAddressViaStorage(ctx, netmapContractKey)
|
||||||
threshold := len(innerRing)/3*2 + 1
|
if !runtime.CheckWitness(multiaddr) {
|
||||||
|
|
||||||
irKey := common.InnerRingInvoker(innerRing)
|
|
||||||
if len(irKey) == 0 {
|
|
||||||
panic("lock: this method must be invoked from inner ring")
|
panic("lock: this method must be invoked from inner ring")
|
||||||
}
|
}
|
||||||
|
|
||||||
hashTxID := common.InvokeID([]interface{}{txID, from, to, amount, until}, []byte("lock"))
|
|
||||||
|
|
||||||
n := common.Vote(ctx, hashTxID, irKey)
|
|
||||||
if n >= threshold {
|
|
||||||
common.RemoveVotes(ctx, hashTxID)
|
|
||||||
|
|
||||||
lockAccount := Account{
|
lockAccount := Account{
|
||||||
Balance: 0,
|
Balance: 0,
|
||||||
Until: until,
|
Until: until,
|
||||||
|
@ -182,27 +150,16 @@ func Lock(txID []byte, from, to interop.Hash160, amount, until int) bool {
|
||||||
|
|
||||||
runtime.Log("lock: created lock account")
|
runtime.Log("lock: created lock account")
|
||||||
runtime.Notify("Lock", txID, from, to, amount, until)
|
runtime.Notify("Lock", txID, from, to, amount, until)
|
||||||
}
|
|
||||||
|
|
||||||
runtime.Log("lock: processed invoke from inner ring")
|
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEpoch(epochNum int) bool {
|
func NewEpoch(epochNum int) bool {
|
||||||
innerRing := irList()
|
multiaddr := common.InnerRingMultiAddressViaStorage(ctx, netmapContractKey)
|
||||||
threshold := len(innerRing)/3*2 + 1
|
if !runtime.CheckWitness(multiaddr) {
|
||||||
|
|
||||||
irKey := common.InnerRingInvoker(innerRing)
|
|
||||||
if len(irKey) == 0 {
|
|
||||||
panic("epochNum: this method must be invoked from inner ring")
|
panic("epochNum: this method must be invoked from inner ring")
|
||||||
}
|
}
|
||||||
|
|
||||||
epochID := common.InvokeID([]interface{}{epochNum}, []byte("epoch"))
|
|
||||||
|
|
||||||
n := common.Vote(ctx, epochID, irKey)
|
|
||||||
if n >= threshold {
|
|
||||||
common.RemoveVotes(ctx, epochID)
|
|
||||||
it := storage.Find(ctx, []byte{}, storage.KeysOnly)
|
it := storage.Find(ctx, []byte{}, storage.KeysOnly)
|
||||||
for iterator.Next(it) {
|
for iterator.Next(it) {
|
||||||
addr := iterator.Value(it).([]byte) // it MUST BE `storage.KeysOnly`
|
addr := iterator.Value(it).([]byte) // it MUST BE `storage.KeysOnly`
|
||||||
|
@ -220,28 +177,16 @@ func NewEpoch(epochNum int) bool {
|
||||||
token.transfer(ctx, addr, acc.Parent, acc.Balance, true, unlockTransferMsg)
|
token.transfer(ctx, addr, acc.Parent, acc.Balance, true, unlockTransferMsg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
runtime.Log("newEpoch: processed invoke from inner ring")
|
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func Mint(to interop.Hash160, amount int, details []byte) bool {
|
func Mint(to interop.Hash160, amount int, details []byte) bool {
|
||||||
innerRing := irList()
|
multiaddr := common.InnerRingMultiAddressViaStorage(ctx, netmapContractKey)
|
||||||
threshold := len(innerRing)/3*2 + 1
|
if !runtime.CheckWitness(multiaddr) {
|
||||||
|
panic("mint: this method must be invoked from inner ring")
|
||||||
irKey := common.InnerRingInvoker(innerRing)
|
|
||||||
if len(irKey) == 0 {
|
|
||||||
panic("burn: this method must be invoked from inner ring")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mintID := common.InvokeID([]interface{}{to, amount, details}, []byte("mint"))
|
|
||||||
|
|
||||||
n := common.Vote(ctx, mintID, irKey)
|
|
||||||
if n >= threshold {
|
|
||||||
common.RemoveVotes(ctx, mintID)
|
|
||||||
|
|
||||||
ok := token.transfer(ctx, nil, to, amount, true, details)
|
ok := token.transfer(ctx, nil, to, amount, true, details)
|
||||||
if !ok {
|
if !ok {
|
||||||
panic("mint: can't transfer assets")
|
panic("mint: can't transfer assets")
|
||||||
|
@ -252,26 +197,16 @@ func Mint(to interop.Hash160, amount int, details []byte) bool {
|
||||||
storage.Put(ctx, token.CirculationKey, supply)
|
storage.Put(ctx, token.CirculationKey, supply)
|
||||||
runtime.Log("mint: assets were minted")
|
runtime.Log("mint: assets were minted")
|
||||||
runtime.Notify("Mint", to, amount)
|
runtime.Notify("Mint", to, amount)
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func Burn(from interop.Hash160, amount int, details []byte) bool {
|
func Burn(from interop.Hash160, amount int, details []byte) bool {
|
||||||
innerRing := irList()
|
multiaddr := common.InnerRingMultiAddressViaStorage(ctx, netmapContractKey)
|
||||||
threshold := len(innerRing)/3*2 + 1
|
if !runtime.CheckWitness(multiaddr) {
|
||||||
|
|
||||||
irKey := common.InnerRingInvoker(innerRing)
|
|
||||||
if len(irKey) == 0 {
|
|
||||||
panic("burn: this method must be invoked from inner ring")
|
panic("burn: this method must be invoked from inner ring")
|
||||||
}
|
}
|
||||||
|
|
||||||
burnID := common.InvokeID([]interface{}{from, amount, details}, []byte("burn"))
|
|
||||||
|
|
||||||
n := common.Vote(ctx, burnID, irKey)
|
|
||||||
if n >= threshold {
|
|
||||||
common.RemoveVotes(ctx, burnID)
|
|
||||||
|
|
||||||
ok := token.transfer(ctx, from, nil, amount, true, details)
|
ok := token.transfer(ctx, from, nil, amount, true, details)
|
||||||
if !ok {
|
if !ok {
|
||||||
panic("burn: can't transfer assets")
|
panic("burn: can't transfer assets")
|
||||||
|
@ -286,7 +221,6 @@ func Burn(from interop.Hash160, amount int, details []byte) bool {
|
||||||
storage.Put(ctx, token.CirculationKey, supply)
|
storage.Put(ctx, token.CirculationKey, supply)
|
||||||
runtime.Log("burn: assets were burned")
|
runtime.Log("burn: assets were burned")
|
||||||
runtime.Notify("Burn", from, amount)
|
runtime.Notify("Burn", from, amount)
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -389,33 +323,3 @@ func getAccount(ctx storage.Context, key interface{}) Account {
|
||||||
|
|
||||||
return Account{}
|
return Account{}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
Check if invocation made from known container or audit contracts.
|
|
||||||
This is necessary because calls from these contracts require to do transfer
|
|
||||||
without signature collection (1 invoke transfer).
|
|
||||||
|
|
||||||
IR1, IR2, IR3, IR4 -(4 invokes)-> [ Container Contract ] -(1 invoke)-> [ Balance Contract ]
|
|
||||||
|
|
||||||
We can do 1 invoke transfer if:
|
|
||||||
- invoke happened from inner ring node,
|
|
||||||
- it is indirect invocation from other smart-contract.
|
|
||||||
|
|
||||||
However there is a possible attack, when malicious inner ring node creates
|
|
||||||
malicious smart-contract in morph chain to do inderect call.
|
|
||||||
|
|
||||||
MaliciousIR -(1 invoke)-> [ Malicious Contract ] -(1 invoke) -> [ Balance Contract ]
|
|
||||||
|
|
||||||
To prevent that, we have to allow 1 invoke transfer from authorised well known
|
|
||||||
smart-contracts, that will be set up at `Init` method.
|
|
||||||
*/
|
|
||||||
|
|
||||||
func fromKnownContract(caller interop.Hash160) bool {
|
|
||||||
containerContractAddr := storage.Get(ctx, containerContractKey).([]byte)
|
|
||||||
|
|
||||||
return common.BytesEqual(caller, containerContractAddr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func irList() []common.IRNode {
|
|
||||||
return common.InnerRingListViaStorage(ctx, netmapContractKey)
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue